Compare commits
2 Commits
1fc1b5d35d
...
86fec254aa
Author | SHA1 | Date | |
---|---|---|---|
86fec254aa | |||
6f16bffe31 |
@ -1,7 +1,8 @@
|
||||
FROM maven:3.6.3-openjdk-16 AS build
|
||||
FROM maven:3-openjdk-23 AS build
|
||||
|
||||
COPY pom.xml /tmp/
|
||||
COPY src /tmp/src/
|
||||
COPY Formatter.xml /tmp/
|
||||
WORKDIR /tmp/
|
||||
RUN mvn clean compile assembly:single
|
||||
|
||||
|
153
back/pom.xml
153
back/pom.xml
@ -5,9 +5,9 @@
|
||||
<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.compiler.version>3.13.0</maven.compiler.version>
|
||||
<maven.compiler.source>23</maven.compiler.source>
|
||||
<maven.compiler.target>23</maven.compiler.target>
|
||||
<maven.dependency.version>3.1.1</maven.dependency.version>
|
||||
</properties>
|
||||
<repositories>
|
||||
@ -20,17 +20,34 @@
|
||||
<dependency>
|
||||
<groupId>kangaroo-and-rabbit</groupId>
|
||||
<artifactId>archidata</artifactId>
|
||||
<version>0.12.0</version>
|
||||
<version>0.23.7-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<!-- Loopback of logger JDK logging API to SLF4J -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>2.1.0-alpha1</version>
|
||||
<artifactId>jul-to-slf4j</artifactId>
|
||||
<version>2.0.9</version>
|
||||
</dependency>
|
||||
<!-- generic logger of SLF4J to console (in color) -->
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.4.11</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>xerces</groupId>
|
||||
<artifactId>xercesImpl</artifactId>
|
||||
<version>2.12.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.janino</groupId>
|
||||
<artifactId>janino</artifactId>
|
||||
<version>3.1.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
<version>2.17.1</version>
|
||||
<version>2.18.1</version>
|
||||
</dependency>
|
||||
<!--
|
||||
************************************************************
|
||||
@ -40,24 +57,24 @@
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>5.11.0-M2</version>
|
||||
<version>5.11.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>5.11.0-M2</version>
|
||||
<version>5.11.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.revelc.code.formatter</groupId>
|
||||
<artifactId>formatter-maven-plugin</artifactId>
|
||||
<version>2.24.0</version>
|
||||
<version>2.24.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>3.3.1</version>
|
||||
<version>3.5.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
@ -87,16 +104,45 @@
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>1.4.0</version>
|
||||
<version>3.2.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>prod-mode</id>
|
||||
<goals>
|
||||
<goal>java</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<mainClass>org.kar.karideo.WebLauncher</mainClass>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>dev-mode</id>
|
||||
<goals>
|
||||
<goal>java</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<mainClass>org.kar.karideo.WebLauncherLocal</mainClass>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>generate-api</id>
|
||||
<goals>
|
||||
<goal>java</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<mainClass>org.kar.karideo.GenerateApi</mainClass>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<mainClass>org.kar.karideo.WebLauncher</mainClass>
|
||||
<mainClass/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- Create the source bundle -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<version>4.0.0-beta-1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
@ -110,10 +156,12 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.0.0-M5</version>
|
||||
<version>3.2.5</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>3.7.1</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
@ -125,80 +173,21 @@
|
||||
</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>
|
||||
<version>3.3.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>net.revelc.code.formatter</groupId>
|
||||
<artifactId>formatter-maven-plugin</artifactId>
|
||||
<version>2.23.0</version>
|
||||
<version>2.24.1</version>
|
||||
<configuration>
|
||||
<encoding>UTF-8</encoding>
|
||||
<lineEnding>LF</lineEnding>
|
||||
@ -229,14 +218,6 @@
|
||||
<configuration>
|
||||
<includeFilterFile>spotbugs-security-include.xml</includeFilterFile>
|
||||
<excludeFilterFile>spotbugs-security-exclude.xml</excludeFilterFile>
|
||||
<!--<plugins>
|
||||
<plugin>
|
||||
<groupId>com.h3xstream.findsecbugs</groupId>
|
||||
<artifactId>findsecbugs-plugin</artifactId>
|
||||
<version>1.12.0</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
-->
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
@ -247,7 +228,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<version>3.3.0</version>
|
||||
<configuration>
|
||||
<show>public</show>
|
||||
</configuration>
|
||||
|
17
back/src/org/kar/karideo/GenerateApi.java
Normal file
17
back/src/org/kar/karideo/GenerateApi.java
Normal file
@ -0,0 +1,17 @@
|
||||
package org.kar.karideo;
|
||||
|
||||
import org.kar.karideo.migration.Initialization;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class GenerateApi {
|
||||
private final static Logger LOGGER = LoggerFactory.getLogger(GenerateApi.class);
|
||||
|
||||
private GenerateApi() {}
|
||||
|
||||
public static void main(final String[] args) throws Exception {
|
||||
LOGGER.info("Generate API");
|
||||
Initialization.generateObjects();
|
||||
LOGGER.info("STOP the REST server.");
|
||||
}
|
||||
}
|
@ -1,21 +1,22 @@
|
||||
package org.kar.karideo;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.logging.LogManager;
|
||||
|
||||
import org.glassfish.grizzly.http.server.HttpServer;
|
||||
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
|
||||
import org.glassfish.jersey.jackson.JacksonFeature;
|
||||
import org.glassfish.jersey.media.multipart.MultiPartFeature;
|
||||
import org.glassfish.jersey.server.ResourceConfig;
|
||||
import org.kar.archidata.GlobalConfiguration;
|
||||
import org.kar.archidata.UpdateJwtPublicKey;
|
||||
import org.kar.archidata.api.DataResource;
|
||||
import org.kar.archidata.catcher.GenericCatcher;
|
||||
import org.kar.archidata.db.DBConfig;
|
||||
import org.kar.archidata.db.DbConfig;
|
||||
import org.kar.archidata.filter.CORSFilter;
|
||||
import org.kar.archidata.filter.OptionFilter;
|
||||
import org.kar.archidata.migration.MigrationEngine;
|
||||
import org.kar.archidata.tools.ConfigBaseVariable;
|
||||
import org.kar.archidata.tools.ContextGenericTools;
|
||||
import org.kar.karideo.api.Front;
|
||||
import org.kar.karideo.api.HealthCheck;
|
||||
import org.kar.karideo.api.MediaResource;
|
||||
@ -30,14 +31,16 @@ import org.kar.karideo.migration.Migration20230810;
|
||||
import org.kar.karideo.migration.Migration20231015;
|
||||
import org.kar.karideo.migration.Migration20231126;
|
||||
import org.kar.karideo.migration.Migration20240226;
|
||||
import org.kar.karideo.migration.Migration20240611;
|
||||
import org.kar.karideo.migration.Migration20250214;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.bridge.SLF4JBridgeHandler;
|
||||
|
||||
import jakarta.ws.rs.core.UriBuilder;
|
||||
|
||||
public class WebLauncher {
|
||||
final static Logger LOGGER = LoggerFactory.getLogger(WebLauncher.class);
|
||||
public static DBConfig dbConfig;
|
||||
protected UpdateJwtPublicKey keyUpdater = null;
|
||||
protected HttpServer server = null;
|
||||
|
||||
@ -59,12 +62,18 @@ public class WebLauncher {
|
||||
migrationEngine.add(new Migration20231015());
|
||||
migrationEngine.add(new Migration20231126());
|
||||
migrationEngine.add(new Migration20240226());
|
||||
migrationEngine.add(new Migration20240611());
|
||||
migrationEngine.add(new Migration20250214());
|
||||
WebLauncher.LOGGER.info("Migrate the DB [START]");
|
||||
migrationEngine.migrateWaitAdmin(GlobalConfiguration.dbConfig);
|
||||
migrationEngine.migrateWaitAdmin(new DbConfig());
|
||||
WebLauncher.LOGGER.info("Migrate the DB [STOP]");
|
||||
}
|
||||
|
||||
public static void main(final String[] args) throws Exception {
|
||||
// Loop-back of logger JDK logging API to SLF4J
|
||||
LogManager.getLogManager().reset();
|
||||
SLF4JBridgeHandler.install();
|
||||
|
||||
WebLauncher.LOGGER.info("[START] application wake UP");
|
||||
final WebLauncher launcher = new WebLauncher();
|
||||
launcher.migrateDB();
|
||||
@ -104,7 +113,7 @@ public class WebLauncher {
|
||||
|
||||
rc.register(HealthCheck.class);
|
||||
rc.register(Front.class);
|
||||
|
||||
ContextGenericTools.addJsr310(rc);
|
||||
// add jackson to be discover when we are ins stand-alone server
|
||||
rc.register(JacksonFeature.class);
|
||||
// enable this to show low level request
|
||||
|
@ -1,40 +1,25 @@
|
||||
|
||||
package org.kar.karideo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.logging.LogManager;
|
||||
|
||||
import org.kar.archidata.api.DataResource;
|
||||
import org.kar.archidata.externalRestApi.AnalyzeApi;
|
||||
import org.kar.archidata.externalRestApi.TsGenerateApi;
|
||||
import org.kar.archidata.tools.ConfigBaseVariable;
|
||||
import org.kar.karideo.api.Front;
|
||||
import org.kar.karideo.api.HealthCheck;
|
||||
import org.kar.karideo.api.MediaResource;
|
||||
import org.kar.karideo.api.SeasonResource;
|
||||
import org.kar.karideo.api.SeriesResource;
|
||||
import org.kar.karideo.api.TypeResource;
|
||||
import org.kar.karideo.api.UserMediaAdvancementResource;
|
||||
import org.kar.karideo.api.UserResource;
|
||||
import org.kar.karideo.migration.Initialization;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.bridge.SLF4JBridgeHandler;
|
||||
|
||||
public class WebLauncherLocal extends WebLauncher {
|
||||
private final static Logger LOGGER = LoggerFactory.getLogger(WebLauncherLocal.class);
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(WebLauncherLocal.class);
|
||||
|
||||
private WebLauncherLocal() {}
|
||||
|
||||
public static void generateObjects() throws Exception {
|
||||
LOGGER.info("Generate APIs");
|
||||
final List<Class<?>> listOfResources = List.of(Front.class, HealthCheck.class, SeasonResource.class, SeriesResource.class, TypeResource.class, UserMediaAdvancementResource.class,
|
||||
UserResource.class, MediaResource.class, DataResource.class);
|
||||
final AnalyzeApi api = new AnalyzeApi();
|
||||
api.addAllApi(listOfResources);
|
||||
TsGenerateApi.generateApi(api, "../front/src/app/back-api/");
|
||||
LOGGER.info("Generate APIs (DONE)");
|
||||
}
|
||||
|
||||
public static void main(final String[] args) throws Exception {
|
||||
generateObjects();
|
||||
// Loop-back of logger JDK logging API to SLF4J
|
||||
LogManager.getLogManager().reset();
|
||||
SLF4JBridgeHandler.install();
|
||||
// Generate the APIs in type-script
|
||||
Initialization.generateObjects();
|
||||
final WebLauncherLocal launcher = new WebLauncherLocal();
|
||||
launcher.process();
|
||||
LOGGER.info("end-configure the server & wait finish process:");
|
||||
@ -48,6 +33,7 @@ public class WebLauncherLocal extends WebLauncher {
|
||||
// for local test:
|
||||
ConfigBaseVariable.apiAdress = "http://0.0.0.0:18080/karideo/api/";
|
||||
ConfigBaseVariable.dbPort = "3906";
|
||||
ConfigBaseVariable.testMode = "true";
|
||||
}
|
||||
try {
|
||||
super.migrateDB();
|
||||
|
@ -11,8 +11,9 @@ import org.glassfish.jersey.media.multipart.FormDataParam;
|
||||
import org.kar.archidata.annotation.AsyncType;
|
||||
import org.kar.archidata.annotation.TypeScriptProgress;
|
||||
import org.kar.archidata.api.DataResource;
|
||||
import org.kar.archidata.dataAccess.DBAccess;
|
||||
import org.kar.archidata.dataAccess.DataAccess;
|
||||
import org.kar.archidata.dataAccess.addOn.AddOnDataJson;
|
||||
import org.kar.archidata.dataAccess.addOnSQL.AddOnDataJson;
|
||||
import org.kar.archidata.exception.FailException;
|
||||
import org.kar.archidata.exception.InputException;
|
||||
import org.kar.archidata.model.Data;
|
||||
@ -97,7 +98,7 @@ public class MediaResource {
|
||||
@FormDataParam("file") final InputStream fileInputStream, //
|
||||
@FormDataParam("file") final FormDataContentDisposition fileMetaData //
|
||||
) throws FailException {
|
||||
try {
|
||||
try (DBAccess db = DBAccess.createInterface()) {
|
||||
// correct input string stream :
|
||||
fileName = multipartCorrection(fileName);
|
||||
universe = multipartCorrection(universe);
|
||||
@ -125,12 +126,12 @@ public class MediaResource {
|
||||
|
||||
final long tmpUID = DataResource.getTmpDataId();
|
||||
final String sha512 = DataResource.saveTemporaryFile(fileInputStream, tmpUID);
|
||||
Data data = DataResource.getWithSha512(sha512);
|
||||
Data data = DataTools.getWithSha512(db, sha512);
|
||||
if (data == null) {
|
||||
LOGGER.info("Need to add the data in the BDD ... ");
|
||||
System.out.flush();
|
||||
try {
|
||||
data = DataResource.createNewData(tmpUID, fileName, sha512);
|
||||
data = DataTools.createNewData(db, tmpUID, fileName, sha512);
|
||||
} catch (final IOException ex) {
|
||||
DataResource.removeTemporaryFile(tmpUID);
|
||||
ex.printStackTrace();
|
||||
@ -139,7 +140,7 @@ public class MediaResource {
|
||||
} else if (data != null && data.deleted != null && data.deleted) {
|
||||
LOGGER.info("Data already exist but deleted");
|
||||
System.out.flush();
|
||||
DataTools.undelete(data.uuid);
|
||||
DataTools.undelete(db, data.oid);
|
||||
data.deleted = false;
|
||||
} else {
|
||||
LOGGER.info("Data already exist ... all good");
|
||||
@ -179,7 +180,7 @@ public class MediaResource {
|
||||
try {
|
||||
final Media media = new Media();
|
||||
media.name = title;
|
||||
media.dataId = data.uuid;
|
||||
media.dataId = data.oid;
|
||||
media.typeId = typeNode.id;
|
||||
media.seriesId = null;
|
||||
if (seriesNode != null) {
|
||||
@ -193,7 +194,7 @@ public class MediaResource {
|
||||
if (episode != null && !episode.contentEquals("")) {
|
||||
media.episode = Integer.parseInt(episode);
|
||||
}
|
||||
final Media out = DataAccess.insert(media);
|
||||
final Media out = db.insert(media);
|
||||
LOGGER.info("Generate new media {}", out);
|
||||
return out;
|
||||
} catch (final SQLException ex) {
|
||||
@ -219,12 +220,13 @@ public class MediaResource {
|
||||
@TypeScriptProgress
|
||||
public Media uploadCover( //
|
||||
@PathParam("id") final Long id, //
|
||||
@FormDataParam("fileName") final String fileName, //
|
||||
@FormDataParam("file") final InputStream fileInputStream, //
|
||||
@FormDataParam("file") final FormDataContentDisposition fileMetaData//
|
||||
) throws Exception {
|
||||
DataTools.uploadCover(Media.class, id, fileName, fileInputStream, fileMetaData);
|
||||
return DataAccess.get(Media.class, id);
|
||||
try (DBAccess dbIo = DBAccess.createInterface()) {
|
||||
DataTools.uploadCover(dbIo, Media.class, id, fileInputStream, fileMetaData);
|
||||
return dbIo.get(Media.class, id);
|
||||
}
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@ -235,8 +237,10 @@ public class MediaResource {
|
||||
@PathParam("id") final Long id, //
|
||||
@PathParam("coverId") final UUID coverId //
|
||||
) throws Exception {
|
||||
AddOnDataJson.removeLink(Media.class, id, "covers", coverId);
|
||||
return DataAccess.get(Media.class, id);
|
||||
try (DBAccess dbIo = DBAccess.createInterface()) {
|
||||
AddOnDataJson.removeLink(dbIo, Media.class, "id", id, "covers", coverId);
|
||||
return dbIo.get(Media.class, id);
|
||||
}
|
||||
}
|
||||
|
||||
@DELETE
|
||||
|
@ -8,10 +8,11 @@ import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
|
||||
import org.glassfish.jersey.media.multipart.FormDataParam;
|
||||
import org.kar.archidata.annotation.AsyncType;
|
||||
import org.kar.archidata.annotation.TypeScriptProgress;
|
||||
import org.kar.archidata.dataAccess.DBAccess;
|
||||
import org.kar.archidata.dataAccess.DataAccess;
|
||||
import org.kar.archidata.dataAccess.QueryAnd;
|
||||
import org.kar.archidata.dataAccess.QueryCondition;
|
||||
import org.kar.archidata.dataAccess.addOn.AddOnDataJson;
|
||||
import org.kar.archidata.dataAccess.addOnSQL.AddOnDataJson;
|
||||
import org.kar.archidata.dataAccess.options.Condition;
|
||||
import org.kar.archidata.tools.DataTools;
|
||||
import org.kar.karideo.model.Season;
|
||||
@ -85,10 +86,12 @@ public class SeasonResource {
|
||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||
@Operation(description = "Upload a new season cover season", tags = "GLOBAL")
|
||||
@TypeScriptProgress
|
||||
public Season uploadCover(@PathParam("id") final Long id, @FormDataParam("fileName") final String fileName, @FormDataParam("file") final InputStream fileInputStream,
|
||||
@FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
|
||||
DataTools.uploadCover(Season.class, id, fileName, fileInputStream, fileMetaData);
|
||||
return DataAccess.get(Season.class, id);
|
||||
public Season uploadCover(@PathParam("id") final Long id, @FormDataParam("file") final InputStream fileInputStream, @FormDataParam("file") final FormDataContentDisposition fileMetaData)
|
||||
throws Exception {
|
||||
try (DBAccess dbIo = DBAccess.createInterface()) {
|
||||
DataTools.uploadCover(dbIo, Season.class, id, fileInputStream, fileMetaData);
|
||||
return dbIo.get(Season.class, id);
|
||||
}
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@ -96,8 +99,10 @@ public class SeasonResource {
|
||||
@RolesAllowed("ADMIN")
|
||||
@Operation(description = "Remove a specific cover of a season", tags = "GLOBAL")
|
||||
public Season removeCover(@PathParam("id") final Long id, @PathParam("coverId") final UUID coverId) throws Exception {
|
||||
AddOnDataJson.removeLink(Season.class, id, "covers", coverId);
|
||||
return DataAccess.get(Season.class, id);
|
||||
try (DBAccess dbIo = DBAccess.createInterface()) {
|
||||
AddOnDataJson.removeLink(dbIo, Season.class, "id", id, "covers", coverId);
|
||||
return dbIo.get(Season.class, id);
|
||||
}
|
||||
}
|
||||
|
||||
public static Season getOrCreate(final String name, final Long seriesId) {
|
||||
|
@ -8,10 +8,11 @@ import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
|
||||
import org.glassfish.jersey.media.multipart.FormDataParam;
|
||||
import org.kar.archidata.annotation.AsyncType;
|
||||
import org.kar.archidata.annotation.TypeScriptProgress;
|
||||
import org.kar.archidata.dataAccess.DBAccess;
|
||||
import org.kar.archidata.dataAccess.DataAccess;
|
||||
import org.kar.archidata.dataAccess.QueryAnd;
|
||||
import org.kar.archidata.dataAccess.QueryCondition;
|
||||
import org.kar.archidata.dataAccess.addOn.AddOnDataJson;
|
||||
import org.kar.archidata.dataAccess.addOnSQL.AddOnDataJson;
|
||||
import org.kar.archidata.dataAccess.options.Condition;
|
||||
import org.kar.archidata.tools.DataTools;
|
||||
import org.kar.karideo.model.Series;
|
||||
@ -85,10 +86,12 @@ public class SeriesResource {
|
||||
@Consumes({ MediaType.MULTIPART_FORM_DATA })
|
||||
@Operation(description = "Upload a new season cover Series", tags = "GLOBAL")
|
||||
@TypeScriptProgress
|
||||
public Series uploadCover(@PathParam("id") final Long id, @FormDataParam("fileName") final String fileName, @FormDataParam("file") final InputStream fileInputStream,
|
||||
@FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
|
||||
DataTools.uploadCover(Series.class, id, fileName, fileInputStream, fileMetaData);
|
||||
return DataAccess.get(Series.class, id);
|
||||
public Series uploadCover(@PathParam("id") final Long id, @FormDataParam("file") final InputStream fileInputStream, @FormDataParam("file") final FormDataContentDisposition fileMetaData)
|
||||
throws Exception {
|
||||
try (DBAccess dbIo = DBAccess.createInterface()) {
|
||||
DataTools.uploadCover(dbIo, Series.class, id, fileInputStream, fileMetaData);
|
||||
return dbIo.get(Series.class, id);
|
||||
}
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@ -96,8 +99,10 @@ public class SeriesResource {
|
||||
@RolesAllowed("ADMIN")
|
||||
@Operation(description = "Remove a specific Series of a season", tags = "GLOBAL")
|
||||
public Series removeCover(@PathParam("id") final Long id, @PathParam("coverId") final UUID coverId) throws Exception {
|
||||
AddOnDataJson.removeLink(Series.class, id, "covers", coverId);
|
||||
return DataAccess.get(Series.class, id);
|
||||
try (DBAccess dbIo = DBAccess.createInterface()) {
|
||||
AddOnDataJson.removeLink(dbIo, Series.class, "id", id, "covers", coverId);
|
||||
return dbIo.get(Series.class, id);
|
||||
}
|
||||
}
|
||||
|
||||
public static Series getOrCreate(final String name, final Long typeId) {
|
||||
|
@ -8,9 +8,10 @@ import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
|
||||
import org.glassfish.jersey.media.multipart.FormDataParam;
|
||||
import org.kar.archidata.annotation.AsyncType;
|
||||
import org.kar.archidata.annotation.TypeScriptProgress;
|
||||
import org.kar.archidata.dataAccess.DBAccess;
|
||||
import org.kar.archidata.dataAccess.DataAccess;
|
||||
import org.kar.archidata.dataAccess.QueryCondition;
|
||||
import org.kar.archidata.dataAccess.addOn.AddOnDataJson;
|
||||
import org.kar.archidata.dataAccess.addOnSQL.AddOnDataJson;
|
||||
import org.kar.archidata.dataAccess.options.Condition;
|
||||
import org.kar.archidata.tools.DataTools;
|
||||
import org.kar.karideo.model.Type;
|
||||
@ -88,10 +89,12 @@ public class TypeResource {
|
||||
@Consumes({ MediaType.MULTIPART_FORM_DATA })
|
||||
@Operation(description = "Upload a new season cover Type", tags = "GLOBAL")
|
||||
@TypeScriptProgress
|
||||
public Type uploadCover(@PathParam("id") final Long id, @FormDataParam("fileName") final String fileName, @FormDataParam("file") final InputStream fileInputStream,
|
||||
@FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
|
||||
DataTools.uploadCover(Type.class, id, fileName, fileInputStream, fileMetaData);
|
||||
return DataAccess.get(Type.class, id);
|
||||
public Type uploadCover(@PathParam("id") final Long id, @FormDataParam("file") final InputStream fileInputStream, @FormDataParam("file") final FormDataContentDisposition fileMetaData)
|
||||
throws Exception {
|
||||
try (DBAccess dbIo = DBAccess.createInterface()) {
|
||||
DataTools.uploadCover(dbIo, Type.class, id, fileInputStream, fileMetaData);
|
||||
return dbIo.get(Type.class, id);
|
||||
}
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@ -99,8 +102,11 @@ public class TypeResource {
|
||||
@RolesAllowed("ADMIN")
|
||||
@Operation(description = "Remove a specific cover of a type", tags = "GLOBAL")
|
||||
public Type removeCover(@PathParam("id") final Long id, @PathParam("coverId") final UUID coverId) throws Exception {
|
||||
AddOnDataJson.removeLink(Type.class, id, "covers", coverId);
|
||||
return DataAccess.get(Type.class, id);
|
||||
|
||||
try (DBAccess dbIo = DBAccess.createInterface()) {
|
||||
AddOnDataJson.removeLink(dbIo, Type.class, "id", id, "covers", coverId);
|
||||
return dbIo.get(Type.class, id);
|
||||
}
|
||||
}
|
||||
|
||||
public static Type getOrCreate(final String name) {
|
||||
|
@ -90,7 +90,7 @@ public class UserMediaAdvancementResource {
|
||||
elem.count++;
|
||||
}
|
||||
LOGGER.info("{},{},{}", elem.time, elem.percent, elem.count);
|
||||
final int nbAfected = DataAccess.update(elem, elem.id, List.of("time", "percent", "count"));
|
||||
final long nbAfected = DataAccess.update(elem, elem.id, List.of("time", "percent", "count"));
|
||||
return DataAccess.get(UserMediaAdvancement.class, elem.id);
|
||||
}
|
||||
|
||||
|
@ -2,10 +2,22 @@ package org.kar.karideo.migration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.kar.archidata.dataAccess.DataAccess;
|
||||
import org.kar.archidata.api.DataResource;
|
||||
import org.kar.archidata.dataAccess.DBAccess;
|
||||
import org.kar.archidata.externalRestApi.AnalyzeApi;
|
||||
import org.kar.archidata.externalRestApi.TsGenerateApi;
|
||||
import org.kar.archidata.migration.MigrationSqlStep;
|
||||
import org.kar.archidata.model.Data;
|
||||
import org.kar.archidata.model.User;
|
||||
import org.kar.archidata.model.token.JwtToken;
|
||||
import org.kar.karideo.api.Front;
|
||||
import org.kar.karideo.api.HealthCheck;
|
||||
import org.kar.karideo.api.MediaResource;
|
||||
import org.kar.karideo.api.SeasonResource;
|
||||
import org.kar.karideo.api.SeriesResource;
|
||||
import org.kar.karideo.api.TypeResource;
|
||||
import org.kar.karideo.api.UserMediaAdvancementResource;
|
||||
import org.kar.karideo.api.UserResource;
|
||||
import org.kar.karideo.model.Media;
|
||||
import org.kar.karideo.model.Season;
|
||||
import org.kar.karideo.model.Series;
|
||||
@ -19,15 +31,24 @@ public class Initialization extends MigrationSqlStep {
|
||||
|
||||
public static final int KARSO_INITIALISATION_ID = 1;
|
||||
|
||||
public static final List<Class<?>> CLASSES_BASE = List.of(Data.class, Media.class, Type.class, Series.class, Season.class, User.class, UserMediaAdvancement.class);
|
||||
public static final List<Class<?>> CLASSES_BASE = List.of(Data.class, Media.class, Type.class, Series.class,
|
||||
Season.class, User.class, UserMediaAdvancement.class);
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Initialization";
|
||||
}
|
||||
|
||||
public Initialization() {
|
||||
|
||||
public static void generateObjects() throws Exception {
|
||||
LOGGER.info("Generate APIs");
|
||||
final List<Class<?>> listOfResources = List.of(Front.class, HealthCheck.class, SeasonResource.class,
|
||||
SeriesResource.class, TypeResource.class, UserMediaAdvancementResource.class, UserResource.class,
|
||||
MediaResource.class, DataResource.class);
|
||||
final AnalyzeApi api = new AnalyzeApi();
|
||||
api.addAllApi(listOfResources);
|
||||
api.addModel(JwtToken.class);
|
||||
TsGenerateApi.generateApi(api, "../front/src/back-api/");
|
||||
LOGGER.info("Generate APIs (DONE)");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -36,19 +57,21 @@ public class Initialization extends MigrationSqlStep {
|
||||
addClass(clazz);
|
||||
}
|
||||
|
||||
addAction("""
|
||||
INSERT INTO `type` (`id`, `name`, `description`) VALUES
|
||||
(UUID_TO_BIN('15237fd7-d4ee-11ee-a8dd-02420a030203'), 'Documentary', 'Documentary (animals, space, earth...)'),
|
||||
(UUID_TO_BIN('553146c1-d4ee-11ee-a8dd-02420a030203'), 'Movie', 'Movie with real humans (film)'),
|
||||
(UUID_TO_BIN('59c430a3-d4ee-11ee-a8dd-02420a030203'), 'Animation', 'Animation movies (film)'),
|
||||
(UUID_TO_BIN('5cd619e3-d4ee-11ee-a8dd-02420a030203'), 'Short movie', 'Small movies (less 2 minutes)'),
|
||||
(UUID_TO_BIN('5fbbf085-d4ee-11ee-a8dd-02420a030203'), 'TV show', 'TV show for old peoples'),
|
||||
(UUID_TO_BIN('66dcb6ba-d4ee-11ee-a8dd-02420a030203'), 'Animation TV show', 'TV show for young peoples'),
|
||||
(UUID_TO_BIN('69ee5c15-d4ee-11ee-a8dd-02420a030203'), 'Theater', 'Theater play'),
|
||||
(UUID_TO_BIN('6ce72530-d4ee-11ee-a8dd-02420a030203'), 'One man show', 'Recorded stand up'),
|
||||
(UUID_TO_BIN('6ff1691a-d4ee-11ee-a8dd-02420a030203'), 'Concert', 'Recorded concert'),
|
||||
(UUID_TO_BIN('730815ef-d4ee-11ee-a8dd-02420a030203'), 'Opera', 'Recorded opera');
|
||||
""");
|
||||
addAction((final DBAccess da) -> {
|
||||
final List<Type> data = List.of(//
|
||||
new Type("Documentary", "Documentary (animals, space, earth...)"), //
|
||||
new Type("Movie", "Movie with real humans (film)"), //
|
||||
new Type("Animation", "Animation movies (film)"), //
|
||||
new Type("Short movie", "Small movies (less 2 minutes)"), //
|
||||
new Type("TV show", "TV show for old peoples"), //
|
||||
new Type("Animation TV show", "TV show for young peoples"), //
|
||||
new Type("Theater", "Theater play"), //
|
||||
new Type("One man show", "Recorded stand up"), //
|
||||
new Type("Concert", "Recorded concert"), //
|
||||
new Type("Opera", "Recorded opera") //
|
||||
);
|
||||
da.insertMultiple(data);
|
||||
});
|
||||
// set start increment element to permit to add after default elements
|
||||
addAction("""
|
||||
ALTER TABLE `media` AUTO_INCREMENT = 1000;
|
||||
@ -67,10 +90,10 @@ public class Initialization extends MigrationSqlStep {
|
||||
""", "mysql");
|
||||
}
|
||||
|
||||
public static void dropAll() {
|
||||
public static void dropAll(final DBAccess da) {
|
||||
for (final Class<?> element : CLASSES_BASE) {
|
||||
try {
|
||||
DataAccess.drop(element);
|
||||
da.drop(element);
|
||||
} catch (final Exception ex) {
|
||||
LOGGER.error("Fail to drop table !!!!!!");
|
||||
ex.printStackTrace();
|
||||
@ -78,10 +101,10 @@ public class Initialization extends MigrationSqlStep {
|
||||
}
|
||||
}
|
||||
|
||||
public static void cleanAll() {
|
||||
public static void cleanAll(final DBAccess da) {
|
||||
for (final Class<?> element : CLASSES_BASE) {
|
||||
try {
|
||||
DataAccess.cleanAll(element);
|
||||
da.cleanAll(element);
|
||||
} catch (final Exception ex) {
|
||||
LOGGER.error("Fail to clean table !!!!!!");
|
||||
ex.printStackTrace();
|
||||
|
@ -1,23 +1,6 @@
|
||||
package org.kar.karideo.migration;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.kar.archidata.api.DataResource;
|
||||
import org.kar.archidata.dataAccess.DataAccess;
|
||||
import org.kar.archidata.dataAccess.addOn.model.LinkTableLongLong;
|
||||
import org.kar.archidata.dataAccess.options.AccessDeletedItems;
|
||||
import org.kar.archidata.dataAccess.options.OverrideTableName;
|
||||
import org.kar.archidata.migration.MigrationSqlStep;
|
||||
import org.kar.archidata.tools.UuidUtils;
|
||||
import org.kar.karideo.migration.model.CoverConversion;
|
||||
import org.kar.karideo.migration.model.MediaConversion;
|
||||
import org.kar.karideo.migration.model.UUIDConversion;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -36,102 +19,6 @@ public class Migration20240226 extends MigrationSqlStep {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateStep() throws Exception {
|
||||
addAction("""
|
||||
ALTER TABLE `data` ADD `uuid` binary(16) AFTER `id`;
|
||||
""");
|
||||
addAction(() -> {
|
||||
final List<UUIDConversion> datas = DataAccess.gets(UUIDConversion.class, new AccessDeletedItems(), new OverrideTableName("data"));
|
||||
for (final UUIDConversion elem : datas) {
|
||||
elem.uuid = UuidUtils.nextUUID();
|
||||
}
|
||||
for (final UUIDConversion elem : datas) {
|
||||
DataAccess.update(elem, elem.id, List.of("uuid"), new OverrideTableName("data"));
|
||||
}
|
||||
});
|
||||
addAction("""
|
||||
ALTER TABLE `data` CHANGE `uuid` `uuid` binary(16) DEFAULT (UUID_TO_BIN(UUID(), TRUE));
|
||||
""");
|
||||
final List<String> tableToTransform = List.of("media", "season", "series", "type", "user");
|
||||
for (final String tableName : tableToTransform) {
|
||||
addAction("ALTER TABLE `" + tableName + "` ADD `covers` text NULL;");
|
||||
addAction(() -> {
|
||||
final List<UUIDConversion> datas = DataAccess.gets(UUIDConversion.class, new AccessDeletedItems(), new OverrideTableName("data"));
|
||||
final List<CoverConversion> medias = DataAccess.gets(CoverConversion.class, new AccessDeletedItems(), new OverrideTableName(tableName));
|
||||
final List<LinkTableLongLong> links = DataAccess.gets(LinkTableLongLong.class, new OverrideTableName(tableName + "_link_cover"));
|
||||
LOGGER.info("Get somes data: {} {} {}", datas.size(), medias.size(), links.size());
|
||||
for (final CoverConversion media : medias) {
|
||||
final List<UUID> values = new ArrayList<>();
|
||||
for (final LinkTableLongLong link : links) {
|
||||
if (link.object1Id.equals(media.id)) {
|
||||
for (final UUIDConversion data : datas) {
|
||||
if (data.id.equals(link.object2Id)) {
|
||||
values.add(data.uuid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (values.size() != 0) {
|
||||
media.covers = values;
|
||||
LOGGER.info(" update: {} => {}", media.id, media.covers);
|
||||
DataAccess.update(media, media.id, List.of("covers"), new OverrideTableName(tableName));
|
||||
}
|
||||
}
|
||||
});
|
||||
addAction("DROP TABLE `" + tableName + "_link_cover`;");
|
||||
}
|
||||
addAction("""
|
||||
ALTER TABLE `media` ADD `dataUUID` binary(16) AFTER dataId;
|
||||
""");
|
||||
addAction(() -> {
|
||||
final List<UUIDConversion> datas = DataAccess.gets(UUIDConversion.class, new AccessDeletedItems(), new OverrideTableName("data"));
|
||||
final List<MediaConversion> medias = DataAccess.gets(MediaConversion.class, new AccessDeletedItems(), new OverrideTableName("media"));
|
||||
for (final MediaConversion media : medias) {
|
||||
for (final UUIDConversion data : datas) {
|
||||
if (data.id.equals(media.dataId)) {
|
||||
media.dataUUID = data.uuid;
|
||||
DataAccess.update(media, media.id, List.of("dataUUID"), new OverrideTableName("media"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
addAction("""
|
||||
ALTER TABLE `media` DROP `dataId`;
|
||||
""");
|
||||
addAction("""
|
||||
ALTER TABLE `media` CHANGE `dataUUID` `dataId` binary(16) NOT NULL;
|
||||
""");
|
||||
// Move the files...
|
||||
addAction(() -> {
|
||||
final List<UUIDConversion> datas = DataAccess.gets(UUIDConversion.class, new AccessDeletedItems(), new OverrideTableName("data"));
|
||||
for (final UUIDConversion data : datas) {
|
||||
final String origin = DataResource.getFileDataOld(data.id);
|
||||
final String destination = DataResource.getFileData(data.uuid);
|
||||
LOGGER.info("move file = {}", origin);
|
||||
LOGGER.info(" ==> {}", destination);
|
||||
try {
|
||||
Files.move(Paths.get(origin), Paths.get(destination), StandardCopyOption.ATOMIC_MOVE);
|
||||
} catch (final NoSuchFileException ex) {
|
||||
LOGGER.error("MOVE_ERROR : {} -> {}", origin, destination);
|
||||
}
|
||||
}
|
||||
});
|
||||
/* I am not sure then I prefer keep the primary key for the moment addAction(""" ALTER TABLE `data` DROP `id`; """); */
|
||||
addAction("""
|
||||
ALTER TABLE `data` CHANGE `id` `idOld` bigint NOT NULL DEFAULT 0;
|
||||
""");
|
||||
addAction("""
|
||||
ALTER TABLE `data` DROP PRIMARY KEY;
|
||||
""");
|
||||
addAction("""
|
||||
ALTER TABLE `data` CHANGE `uuid` `id` binary(16) DEFAULT (UUID_TO_BIN(UUID(), TRUE));
|
||||
""");
|
||||
addAction("""
|
||||
ALTER TABLE `data` ADD PRIMARY KEY `id` (`id`);
|
||||
""");
|
||||
}
|
||||
public void generateStep() throws Exception {}
|
||||
|
||||
}
|
||||
|
28
back/src/org/kar/karideo/migration/Migration20240611.java
Normal file
28
back/src/org/kar/karideo/migration/Migration20240611.java
Normal file
@ -0,0 +1,28 @@
|
||||
package org.kar.karideo.migration;
|
||||
|
||||
import org.kar.archidata.migration.MigrationSqlStep;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Migration20240611 extends MigrationSqlStep {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Migration20240226.class);
|
||||
|
||||
public static final int KARSO_INITIALISATION_ID = 1;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "migration-2024-06-11: corect data uuid";
|
||||
}
|
||||
|
||||
public Migration20240611() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateStep() throws Exception {
|
||||
addAction("""
|
||||
ALTER TABLE `data` CHANGE `id` `uuid` binary(16) DEFAULT (UUID_TO_BIN(UUID(), TRUE));
|
||||
""");
|
||||
}
|
||||
|
||||
}
|
126
back/src/org/kar/karideo/migration/Migration20250214.java
Normal file
126
back/src/org/kar/karideo/migration/Migration20250214.java
Normal file
@ -0,0 +1,126 @@
|
||||
package org.kar.karideo.migration;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
import org.kar.archidata.api.DataResource;
|
||||
import org.kar.archidata.dataAccess.DBAccess;
|
||||
import org.kar.archidata.dataAccess.options.AccessDeletedItems;
|
||||
import org.kar.archidata.dataAccess.options.OverrideTableName;
|
||||
import org.kar.archidata.migration.MigrationSqlStep;
|
||||
import org.kar.karideo.migration.model.CoverConversion;
|
||||
import org.kar.karideo.migration.model.MediaConversion;
|
||||
import org.kar.karideo.migration.model.OIDConversion;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Migration20250214 extends MigrationSqlStep {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Migration20240226.class);
|
||||
|
||||
public static final int KARSO_INITIALISATION_ID = 1;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "migration-2025-01-04: convert base from UUID to OID";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateStep() throws Exception {
|
||||
|
||||
addAction("""
|
||||
ALTER TABLE `data` ADD `_id` binary(12) AFTER `uuid`;
|
||||
""");
|
||||
addAction((final DBAccess da) -> {
|
||||
final List<OIDConversion> datas = da.gets(OIDConversion.class, new AccessDeletedItems(), new OverrideTableName("data"));
|
||||
for (final OIDConversion elem : datas) {
|
||||
elem._id = new ObjectId();
|
||||
}
|
||||
for (final OIDConversion elem : datas) {
|
||||
da.update(elem, elem.uuid, List.of("_id"), new OverrideTableName("data"));
|
||||
}
|
||||
});
|
||||
final List<String> tableToTransform = List.of("media", "season", "series", "type", "user");
|
||||
for (final String tableName : tableToTransform) {
|
||||
addAction("ALTER TABLE `" + tableName + "` ADD `covers_oid` text NULL;");
|
||||
addAction((final DBAccess da) -> {
|
||||
final List<OIDConversion> datas = da.gets(OIDConversion.class, new AccessDeletedItems(), new OverrideTableName("data"));
|
||||
final List<CoverConversion> tableCoverTransforms = da.gets(CoverConversion.class, new AccessDeletedItems(), new OverrideTableName(tableName));
|
||||
LOGGER.info("Get somes data: {} {}", datas.size(), tableCoverTransforms.size());
|
||||
for (final CoverConversion tableTransform : tableCoverTransforms) {
|
||||
final List<ObjectId> values = new ArrayList<>();
|
||||
if (tableTransform.covers == null) {
|
||||
continue;
|
||||
}
|
||||
for (final UUID link : tableTransform.covers) {
|
||||
for (final OIDConversion data : datas) {
|
||||
if (data.uuid.equals(link)) {
|
||||
values.add(data._id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (values.size() != 0) {
|
||||
tableTransform.covers_oid = values;
|
||||
LOGGER.info(" update: {}: {} => {}", tableTransform.id, tableTransform.covers, tableTransform.covers_oid);
|
||||
da.update(tableTransform, tableTransform.id, List.of("covers_oid"), new OverrideTableName(tableName));
|
||||
}
|
||||
}
|
||||
});
|
||||
addAction("ALTER TABLE `" + tableName + "` DROP `covers`;");
|
||||
addAction("ALTER TABLE `" + tableName + "` CHANGE `covers_oid` `covers` text NULL;");
|
||||
}
|
||||
addAction("""
|
||||
ALTER TABLE `media` ADD `dataOid` binary(12) AFTER dataId;
|
||||
""");
|
||||
addAction((final DBAccess da) -> {
|
||||
final List<OIDConversion> datas = da.gets(OIDConversion.class, new AccessDeletedItems(), new OverrideTableName("data"));
|
||||
final List<MediaConversion> medias = da.gets(MediaConversion.class, new AccessDeletedItems(), new OverrideTableName("media"));
|
||||
for (final MediaConversion media : medias) {
|
||||
for (final OIDConversion data : datas) {
|
||||
if (data.uuid.equals(media.dataId)) {
|
||||
media.dataOid = data._id;
|
||||
da.update(media, media.id, List.of("dataOid"), new OverrideTableName("media"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
addAction("""
|
||||
ALTER TABLE `media` DROP `dataId`;
|
||||
""");
|
||||
addAction("""
|
||||
ALTER TABLE `media` CHANGE `dataOid` `dataId` binary(12) NOT NULL;
|
||||
""");
|
||||
// Move the files...
|
||||
addAction((final DBAccess da) -> {
|
||||
final List<OIDConversion> datas = da.gets(OIDConversion.class, new AccessDeletedItems(), new OverrideTableName("data"));
|
||||
for (final OIDConversion data : datas) {
|
||||
final String origin = DataResource.getFileDataOld(data.uuid);
|
||||
final String destination = DataResource.getFileData(data._id);
|
||||
LOGGER.info("move file = {}", origin);
|
||||
LOGGER.info(" ==> {}", destination);
|
||||
try {
|
||||
Files.move(Paths.get(origin), Paths.get(destination), StandardCopyOption.ATOMIC_MOVE);
|
||||
} catch (final NoSuchFileException ex) {
|
||||
LOGGER.warn("Fail to move file : {}", ex.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
addAction("""
|
||||
ALTER TABLE `data` DROP INDEX `PRIMARY`;
|
||||
""");
|
||||
// addAction("""
|
||||
// ALTER TABLE `data` DROP `uuid`;
|
||||
// """);
|
||||
addAction("""
|
||||
ALTER TABLE `data` ADD PRIMARY KEY `_id` (`_id`);
|
||||
""");
|
||||
}
|
||||
|
||||
}
|
@ -3,6 +3,7 @@ package org.kar.karideo.migration.model;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
import org.kar.archidata.annotation.DataJson;
|
||||
|
||||
import jakarta.persistence.Id;
|
||||
@ -12,4 +13,6 @@ public class CoverConversion {
|
||||
public Long id = null;
|
||||
@DataJson
|
||||
public List<UUID> covers = null;
|
||||
@DataJson
|
||||
public List<ObjectId> covers_oid = null;
|
||||
}
|
||||
|
@ -2,11 +2,12 @@ package org.kar.karideo.migration.model;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
import jakarta.persistence.Id;
|
||||
|
||||
public class MediaConversion {
|
||||
@Id
|
||||
public Long id = null;
|
||||
public Long dataId = null;
|
||||
public UUID dataUUID = null;
|
||||
public UUID dataId = null;
|
||||
public ObjectId dataOid = null;
|
||||
}
|
||||
|
@ -2,10 +2,12 @@ package org.kar.karideo.migration.model;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
import jakarta.persistence.Id;
|
||||
|
||||
public class UUIDConversion {
|
||||
public class OIDConversion {
|
||||
@Id
|
||||
public Long id = null;
|
||||
public UUID uuid = null;
|
||||
public ObjectId _id = null;
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
package org.kar.karideo.model;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
import org.kar.archidata.annotation.DataJson;
|
||||
import org.kar.archidata.model.Data;
|
||||
import org.kar.archidata.model.GenericDataSoftDelete;
|
||||
@ -10,6 +10,7 @@ import org.kar.archidata.model.GenericDataSoftDelete;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
@ -29,7 +30,7 @@ public class Media extends GenericDataSoftDelete {
|
||||
@Schema(description = "Foreign Key Id of the data")
|
||||
@ManyToOne(fetch = FetchType.LAZY, targetEntity = Data.class)
|
||||
@Column(nullable = false)
|
||||
public UUID dataId;
|
||||
public ObjectId dataId;
|
||||
@Schema(description = "Type of the media")
|
||||
@ManyToOne(fetch = FetchType.LAZY, targetEntity = Type.class)
|
||||
public Long typeId;
|
||||
@ -49,7 +50,8 @@ public class Media extends GenericDataSoftDelete {
|
||||
public Integer ageLimit;
|
||||
@Schema(description = "List of Id of the specific covers")
|
||||
@DataJson(targetEntity = Data.class)
|
||||
public List<UUID> covers = null;
|
||||
@Nullable
|
||||
public List<ObjectId> covers = null;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
@ -1,8 +1,8 @@
|
||||
package org.kar.karideo.model;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
import org.kar.archidata.annotation.DataIfNotExists;
|
||||
import org.kar.archidata.annotation.DataJson;
|
||||
import org.kar.archidata.model.GenericDataSoftDelete;
|
||||
@ -10,6 +10,7 @@ import org.kar.archidata.model.GenericDataSoftDelete;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
@ -31,5 +32,6 @@ public class Season extends GenericDataSoftDelete {
|
||||
public Long parentId;
|
||||
@Schema(description = "List of Id of the specific covers")
|
||||
@DataJson()
|
||||
public List<UUID> covers = null;
|
||||
@Nullable
|
||||
public List<ObjectId> covers = null;
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
package org.kar.karideo.model;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
import org.kar.archidata.annotation.DataIfNotExists;
|
||||
import org.kar.archidata.annotation.DataJson;
|
||||
import org.kar.archidata.model.GenericDataSoftDelete;
|
||||
@ -10,6 +10,7 @@ import org.kar.archidata.model.GenericDataSoftDelete;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
@ -31,5 +32,6 @@ public class Series extends GenericDataSoftDelete {
|
||||
public Long parentId;
|
||||
@Schema(description = "List of Id of the specific covers")
|
||||
@DataJson()
|
||||
public List<UUID> covers = null;
|
||||
@Nullable
|
||||
public List<ObjectId> covers = null;
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
package org.kar.karideo.model;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
import org.kar.archidata.annotation.DataIfNotExists;
|
||||
import org.kar.archidata.annotation.DataJson;
|
||||
import org.kar.archidata.model.GenericDataSoftDelete;
|
||||
@ -10,6 +10,7 @@ import org.kar.archidata.model.GenericDataSoftDelete;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@ -25,5 +26,14 @@ public class Type extends GenericDataSoftDelete {
|
||||
public String description;
|
||||
@Schema(description = "List of Id of the specific covers")
|
||||
@DataJson()
|
||||
public List<UUID> covers = null;
|
||||
@Nullable
|
||||
public List<ObjectId> covers = null;
|
||||
|
||||
public Type() {}
|
||||
|
||||
public Type(final String name, final String description) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import org.kar.archidata.model.GenericDataSoftDelete;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
@ -20,6 +21,7 @@ public class UserMediaAdvancement extends GenericDataSoftDelete {
|
||||
@Column(nullable = false)
|
||||
@Schema(description = "Foreign Key Id of the user")
|
||||
@ManyToOne(fetch = FetchType.LAZY, targetEntity = UserKarideo.class)
|
||||
@Nullable
|
||||
public Long userId;
|
||||
@Column(nullable = false)
|
||||
@Schema(description = "Id of the media")
|
||||
|
128
back/test/src/test/kar/karideo/ConfigureDb.java
Normal file
128
back/test/src/test/kar/karideo/ConfigureDb.java
Normal file
@ -0,0 +1,128 @@
|
||||
package test.kar.karideo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.kar.archidata.dataAccess.DBAccess;
|
||||
import org.kar.archidata.db.DbConfig;
|
||||
import org.kar.archidata.db.DbIoFactory;
|
||||
import org.kar.archidata.exception.DataAccessException;
|
||||
import org.kar.archidata.tools.ConfigBaseVariable;
|
||||
import org.kar.karideo.model.Media;
|
||||
import org.kar.karideo.model.Season;
|
||||
import org.kar.karideo.model.Series;
|
||||
import org.kar.karideo.model.Type;
|
||||
import org.kar.karideo.model.UserKarideo;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jakarta.ws.rs.InternalServerErrorException;
|
||||
|
||||
public class ConfigureDb {
|
||||
final static private Logger LOGGER = LoggerFactory.getLogger(ConfigureDb.class);
|
||||
final static private String modeTestForced = null;// "MONGO";
|
||||
public static DBAccess da = null;
|
||||
|
||||
public static void configure() throws IOException, InternalServerErrorException, DataAccessException {
|
||||
String modeTest = System.getenv("TEST_E2E_MODE");
|
||||
if (modeTest == null || modeTest.isEmpty() || "false".equalsIgnoreCase(modeTest)) {
|
||||
modeTest = "SQLITE-MEMORY";
|
||||
} else if ("true".equalsIgnoreCase(modeTest)) {
|
||||
modeTest = "MY-SQL";
|
||||
}
|
||||
// override the local test:
|
||||
if (modeTestForced != null) {
|
||||
modeTest = modeTestForced;
|
||||
}
|
||||
// for local test:
|
||||
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).
|
||||
ConfigBaseVariable.testMode = "true";
|
||||
final List<Class<?>> listObject = List.of( //
|
||||
Media.class, //
|
||||
Season.class, //
|
||||
Series.class, //
|
||||
Type.class, //
|
||||
UserKarideo.class //
|
||||
);
|
||||
if ("SQLITE-MEMORY".equalsIgnoreCase(modeTest)) {
|
||||
ConfigBaseVariable.dbType = "sqlite";
|
||||
ConfigBaseVariable.bdDatabase = null;
|
||||
ConfigBaseVariable.dbHost = "memory";
|
||||
// for test we need to connect all time the DB
|
||||
ConfigBaseVariable.dbKeepConnected = "true";
|
||||
} else if ("SQLITE".equalsIgnoreCase(modeTest)) {
|
||||
ConfigBaseVariable.dbType = "sqlite";
|
||||
ConfigBaseVariable.bdDatabase = null;
|
||||
ConfigBaseVariable.dbKeepConnected = "true";
|
||||
} else if ("MY-SQL".equalsIgnoreCase(modeTest)) {
|
||||
ConfigBaseVariable.dbType = "mysql";
|
||||
ConfigBaseVariable.bdDatabase = "test_Karideo_db";
|
||||
ConfigBaseVariable.dbPort = "3906";
|
||||
ConfigBaseVariable.dbUser = "root";
|
||||
} else if ("MONGO".equalsIgnoreCase(modeTest)) {
|
||||
ConfigBaseVariable.dbType = "mongo";
|
||||
ConfigBaseVariable.bdDatabase = "test_Karideo_db";
|
||||
} else {
|
||||
// User local modification ...
|
||||
ConfigBaseVariable.bdDatabase = "test_Karideo_db";
|
||||
ConfigBaseVariable.dbPort = "3906";
|
||||
ConfigBaseVariable.dbUser = "root";
|
||||
}
|
||||
removeDB();
|
||||
// Connect the dataBase...
|
||||
da = DBAccess.createInterface();
|
||||
}
|
||||
|
||||
public static void removeDB() {
|
||||
String modeTest = System.getenv("TEST_E2E_MODE");
|
||||
if (modeTest == null || modeTest.isEmpty() || "false".equalsIgnoreCase(modeTest)) {
|
||||
modeTest = "SQLITE-MEMORY";
|
||||
} else if ("true".equalsIgnoreCase(modeTest)) {
|
||||
modeTest = "MY-SQL";
|
||||
}
|
||||
// override the local test:
|
||||
if (modeTestForced != null) {
|
||||
modeTest = modeTestForced;
|
||||
}
|
||||
DbConfig config = null;
|
||||
try {
|
||||
config = new DbConfig();
|
||||
} catch (final DataAccessException e) {
|
||||
e.printStackTrace();
|
||||
LOGGER.error("Fail to clean the DB");
|
||||
return;
|
||||
}
|
||||
config.setDbName(null);
|
||||
LOGGER.info("Remove the DB and create a new one '{}'", config.getDbName());
|
||||
try (final DBAccess daRoot = DBAccess.createInterface(config)) {
|
||||
if ("SQLITE-MEMORY".equalsIgnoreCase(modeTest)) {
|
||||
// nothing to do ...
|
||||
} else if ("SQLITE".equalsIgnoreCase(modeTest)) {
|
||||
daRoot.deleteDB(ConfigBaseVariable.bdDatabase);
|
||||
} else if ("MY-SQL".equalsIgnoreCase(modeTest)) {
|
||||
daRoot.deleteDB(ConfigBaseVariable.bdDatabase);
|
||||
} else if ("MONGO".equalsIgnoreCase(modeTest)) {
|
||||
daRoot.deleteDB(ConfigBaseVariable.bdDatabase);
|
||||
}
|
||||
daRoot.createDB(ConfigBaseVariable.bdDatabase);
|
||||
} catch (final InternalServerErrorException e) {
|
||||
e.printStackTrace();
|
||||
LOGGER.error("Fail to clean the DB");
|
||||
return;
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
LOGGER.error("Fail to clean the DB");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public static void clear() throws IOException {
|
||||
LOGGER.info("Remove the test db");
|
||||
removeDB();
|
||||
// The connection is by default open ==> close it at the end of test:
|
||||
da.close();
|
||||
DbIoFactory.closeAllForceMode();
|
||||
ConfigBaseVariable.clearAllValue();
|
||||
}
|
||||
}
|
48
back/test/src/test/kar/karideo/TestBase.java
Normal file
48
back/test/src/test/kar/karideo/TestBase.java
Normal file
@ -0,0 +1,48 @@
|
||||
package test.kar.karideo;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.MethodOrderer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.kar.archidata.tools.ConfigBaseVariable;
|
||||
import org.kar.archidata.tools.RESTApi;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ExtendWith(StepwiseExtension.class)
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
public class TestBase {
|
||||
private final static Logger LOGGER = LoggerFactory.getLogger(TestBase.class);
|
||||
public final static String ENDPOINT_NAME = "species/";
|
||||
|
||||
static WebLauncherTest webInterface = null;
|
||||
static RESTApi api = null;
|
||||
|
||||
@BeforeAll
|
||||
public static void configureWebServer() throws Exception {
|
||||
ConfigureDb.configure();
|
||||
LOGGER.info("configure server ...");
|
||||
webInterface = new WebLauncherTest();
|
||||
LOGGER.info("Start REST (BEGIN)");
|
||||
webInterface.process();
|
||||
LOGGER.info("Start REST (DONE)");
|
||||
api = new RESTApi(ConfigBaseVariable.apiAdress);
|
||||
api.setToken(Common.ADMIN_TOKEN);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void stopWebServer() throws Exception {
|
||||
LOGGER.info("Kill the web server");
|
||||
webInterface.stop();
|
||||
webInterface = null;
|
||||
ConfigureDb.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public static void TestEmpty() throws Exception {
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -10,8 +10,7 @@ import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.kar.archidata.db.DBEntry;
|
||||
import org.kar.archidata.exception.RESTErrorResponseExeption;
|
||||
import org.kar.archidata.exception.RESTErrorResponseException;
|
||||
import org.kar.archidata.tools.ConfigBaseVariable;
|
||||
import org.kar.archidata.tools.RESTApi;
|
||||
import org.kar.karideo.api.HealthCheck.HealthResult;
|
||||
@ -49,7 +48,7 @@ public class TestHealthCheck {
|
||||
webInterface.stop();
|
||||
webInterface = null;
|
||||
LOGGER.info("Remove the test db");
|
||||
DBEntry.closeAllForceMode();
|
||||
// DBEntry.closeAllForceMode();
|
||||
ConfigBaseVariable.clearAllValue();
|
||||
}
|
||||
|
||||
@ -64,7 +63,7 @@ public class TestHealthCheck {
|
||||
@Order(2)
|
||||
@Test
|
||||
public void checkHealthCheckWrongAPI() throws Exception {
|
||||
Assertions.assertThrows(RESTErrorResponseExeption.class, () -> api.get(HealthResult.class, "health_checks"));
|
||||
Assertions.assertThrows(RESTErrorResponseException.class, () -> api.get(HealthResult.class, "health_checks"));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
|
||||
package test.kar.karideo;
|
||||
|
||||
import org.kar.archidata.tools.ConfigBaseVariable;
|
||||
import org.kar.karideo.WebLauncher;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -9,26 +8,5 @@ import org.slf4j.LoggerFactory;
|
||||
public class WebLauncherTest extends WebLauncher {
|
||||
final private static Logger LOGGER = LoggerFactory.getLogger(WebLauncherTest.class);
|
||||
|
||||
public WebLauncherTest() {
|
||||
LOGGER.debug("Configure REST system");
|
||||
// for local test:
|
||||
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).
|
||||
ConfigBaseVariable.testMode = "true";
|
||||
// for the test we a in memory sqlite..
|
||||
if (true) {
|
||||
if (!"true".equalsIgnoreCase(System.getenv("TEST_E2E_MODE"))) {
|
||||
ConfigBaseVariable.dbType = "sqlite";
|
||||
ConfigBaseVariable.dbHost = "memory";
|
||||
// for test we need to connect all time the DB
|
||||
ConfigBaseVariable.dbKeepConnected = "true";
|
||||
}
|
||||
} else {
|
||||
// Enable this if you want to access to a local MySQL base to test with an adminer
|
||||
ConfigBaseVariable.bdDatabase = "test_db";
|
||||
ConfigBaseVariable.dbPort = "3309";
|
||||
ConfigBaseVariable.dbUser = "root";
|
||||
ConfigBaseVariable.dbPassword = "password";
|
||||
}
|
||||
}
|
||||
public WebLauncherTest() {}
|
||||
}
|
||||
|
1
front/.env
Normal file
1
front/.env
Normal file
@ -0,0 +1 @@
|
||||
NODE_ENV=development
|
2
front/.env.production
Normal file
2
front/.env.production
Normal file
@ -0,0 +1,2 @@
|
||||
# URL for database connection
|
||||
VITE_API_BASE_URL=karusic/api/
|
27
front/.storybook/main.ts
Normal file
27
front/.storybook/main.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import type { StorybookConfig } from '@storybook/react-vite';
|
||||
|
||||
const config: StorybookConfig = {
|
||||
framework: {
|
||||
name: '@storybook/react-vite',
|
||||
options: {},
|
||||
},
|
||||
|
||||
core: {
|
||||
disableTelemetry: true,
|
||||
builder: '@storybook/builder-vite',
|
||||
},
|
||||
|
||||
stories: ['../src/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
|
||||
|
||||
addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
|
||||
|
||||
staticDirs: ['../public'],
|
||||
|
||||
typescript: {
|
||||
reactDocgen: false,
|
||||
},
|
||||
|
||||
docs: {},
|
||||
};
|
||||
|
||||
export default config;
|
16
front/.storybook/preview-head.html
Normal file
16
front/.storybook/preview-head.html
Normal file
@ -0,0 +1,16 @@
|
||||
<style>
|
||||
html {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.docs-story > :first-child {
|
||||
padding: 0;
|
||||
}
|
||||
.docs-story > * {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
#root #start-ui-storybook-wrapper {
|
||||
min-height: 100vh;
|
||||
}
|
||||
</style>
|
34
front/.storybook/preview.tsx
Normal file
34
front/.storybook/preview.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { ChakraProvider } from '@chakra-ui/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
import { ColorModeProvider } from '../src/components/ui/color-mode';
|
||||
import { Toaster } from '../src/components/ui/toaster';
|
||||
import { systemTheme } from '../src/theme/theme';
|
||||
|
||||
// .
|
||||
const DocumentationWrapper = ({ children }) => {
|
||||
return (
|
||||
<Box id="start-ui-storybook-wrapper" p="4" pb="8" flex="1">
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export const decorators = [
|
||||
(Story, context) => (
|
||||
<ColorModeProvider>
|
||||
<ChakraProvider value={systemTheme}>
|
||||
{/* Using MemoryRouter to avoid route clashing with Storybook */}
|
||||
<MemoryRouter>
|
||||
<DocumentationWrapper>
|
||||
<Story {...context} />
|
||||
</DocumentationWrapper>
|
||||
</MemoryRouter>
|
||||
<Toaster />
|
||||
</ChakraProvider>
|
||||
</ColorModeProvider>
|
||||
),
|
||||
];
|
2
front/LICENSE
Normal file
2
front/LICENSE
Normal file
@ -0,0 +1,2 @@
|
||||
Proprietary
|
||||
@copyright Edouard Dupin 2024
|
10637
front/config sample.yaml
Normal file
10637
front/config sample.yaml
Normal file
File diff suppressed because it is too large
Load Diff
13
front/index.html
Normal file
13
front/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Karideo</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</head>
|
||||
<body style="width:100vw;height:100vh;min-width:100%;min-height:100%;">
|
||||
<div id="root" style="width:100%;height:100%;min-width:100%;min-height:100%;"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
9
front/knip.ts
Normal file
9
front/knip.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import type { KnipConfig } from 'knip';
|
||||
|
||||
const config: KnipConfig = {
|
||||
// Ignoring mostly shell binaries
|
||||
ignoreBinaries: ['export', 'sleep'],
|
||||
ignore: [],
|
||||
};
|
||||
|
||||
export default config;
|
@ -1,50 +1,91 @@
|
||||
{
|
||||
"name": "karideo",
|
||||
"version": "0.0.0",
|
||||
"license": "MPL-2",
|
||||
"scripts": {
|
||||
"all": "npm run build && npm run test",
|
||||
"ng": "ng",
|
||||
"dev": "ng serve --configuration=develop --watch --port 4202",
|
||||
"dev-hot-update": "ng serve --configuration=develop --watch --hmr --port 4202",
|
||||
"build": "ng build --prod",
|
||||
"test": "ng test",
|
||||
"lint": "ng lint",
|
||||
"style": "prettier --write .",
|
||||
"e2e": "ng e2e",
|
||||
"update_packages": "ncu --upgrade",
|
||||
"install_dependency": "pnpm install --force",
|
||||
"link_kar_cw": "pnpm link ../../kar-cw/dist/kar-cw/",
|
||||
"unlink_kar_cw": "pnpm unlink ../../kar-cw/dist/kar-cw/"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^18.0.2",
|
||||
"@angular/cdk": "^18.0.2",
|
||||
"@angular/common": "^18.0.2",
|
||||
"@angular/compiler": "^18.0.2",
|
||||
"@angular/core": "^18.0.2",
|
||||
"@angular/forms": "^18.0.2",
|
||||
"@angular/material": "^18.0.2",
|
||||
"@angular/platform-browser": "^18.0.2",
|
||||
"@angular/platform-browser-dynamic": "^18.0.2",
|
||||
"@angular/router": "^18.0.2",
|
||||
"rxjs": "^7.8.1",
|
||||
"zone.js": "^0.14.6",
|
||||
"zod": "3.23.8",
|
||||
"@kangaroo-and-rabbit/kar-cw": "^0.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^18.0.3",
|
||||
"@angular-eslint/builder": "18.0.1",
|
||||
"@angular-eslint/eslint-plugin": "18.0.1",
|
||||
"@angular-eslint/eslint-plugin-template": "18.0.1",
|
||||
"@angular-eslint/schematics": "18.0.1",
|
||||
"@angular-eslint/template-parser": "18.0.1",
|
||||
"@angular/cli": "^18.0.3",
|
||||
"@angular/compiler-cli": "^18.0.2",
|
||||
"@angular/language-service": "^18.0.2",
|
||||
"npm-check-updates": "^16.14.20",
|
||||
"tslib": "^2.6.3"
|
||||
}
|
||||
"name": "karideo",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"description": "KAR web music application",
|
||||
"author": {
|
||||
"name": "Edouard DUPIN",
|
||||
"email": "yui.heero@gmail.farm"
|
||||
},
|
||||
"license": "PROPRIETARY",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"scripts": {
|
||||
"update_packages": "ncu --target minor",
|
||||
"upgrade_packages": "ncu --upgrade ",
|
||||
"install_dependency": "pnpm install",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest watch",
|
||||
"build": "tsc && vite build",
|
||||
"static:build": "pnpm build",
|
||||
"dev": "vite",
|
||||
"pretty": "prettier -w .",
|
||||
"lint": "pnpm tsc --noEmit",
|
||||
"storybook": "storybook dev -p 3001",
|
||||
"storybook:build": "storybook build && mv ./storybook-static ./public/storybook"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{ts,tsx,js,jsx,json}": "prettier --write"
|
||||
},
|
||||
"dependencies": {
|
||||
"@trivago/prettier-plugin-sort-imports": "5.2.2",
|
||||
"@chakra-ui/cli": "3.7.0",
|
||||
"@chakra-ui/react": "3.7.0",
|
||||
"@emotion/react": "11.14.0",
|
||||
"allotment": "1.20.2",
|
||||
"css-mediaquery": "0.1.2",
|
||||
"dayjs": "1.11.13",
|
||||
"history": "5.3.0",
|
||||
"next-themes": "^0.4.4",
|
||||
"react": "19.0.0-rc.1",
|
||||
"react-dom": "19.0.0-rc.1",
|
||||
"react-error-boundary": "5.0.0",
|
||||
"react-icons": "5.4.0",
|
||||
"react-router-dom": "7.1.5",
|
||||
"react-select": "5.10.0",
|
||||
"react-use": "17.6.0",
|
||||
"zod": "3.24.1",
|
||||
"zustand": "5.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@chakra-ui/styled-system": "^2.12.0",
|
||||
"@playwright/test": "1.50.1",
|
||||
"@storybook/addon-actions": "8.5.4",
|
||||
"@storybook/addon-essentials": "8.5.4",
|
||||
"@storybook/addon-links": "8.5.4",
|
||||
"@storybook/addon-mdx-gfm": "8.5.4",
|
||||
"@storybook/react": "8.5.4",
|
||||
"@storybook/react-vite": "8.5.4",
|
||||
"@storybook/theming": "8.5.4",
|
||||
"@testing-library/jest-dom": "6.6.3",
|
||||
"@testing-library/react": "16.2.0",
|
||||
"@testing-library/user-event": "14.6.1",
|
||||
"@trivago/prettier-plugin-sort-imports": "5.2.2",
|
||||
"@types/jest": "29.5.14",
|
||||
"@types/node": "22.13.1",
|
||||
"@types/react": "19.0.8",
|
||||
"@types/react-dom": "19.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "8.24.0",
|
||||
"@typescript-eslint/parser": "8.24.0",
|
||||
"@vitejs/plugin-react": "4.3.4",
|
||||
"eslint": "9.20.1",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-react": "7.37.4",
|
||||
"eslint-plugin-react-hooks": "5.1.0",
|
||||
"eslint-plugin-storybook": "0.11.2",
|
||||
"jest": "29.7.0",
|
||||
"jest-environment-jsdom": "29.7.0",
|
||||
"knip": "5.44.0",
|
||||
"lint-staged": "15.4.3",
|
||||
"npm-check-updates": "^17.1.14",
|
||||
"prettier": "3.5.0",
|
||||
"puppeteer": "24.2.0",
|
||||
"react-is": "19.0.0",
|
||||
"storybook": "8.5.4",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "5.7.3",
|
||||
"vite": "6.1.0",
|
||||
"vitest": "3.0.5"
|
||||
}
|
||||
}
|
17692
front/pnpm-lock.yaml
generated
17692
front/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
16
front/prettier.config.js
Normal file
16
front/prettier.config.js
Normal file
@ -0,0 +1,16 @@
|
||||
// Using a JS file, allowing us to add comments
|
||||
module.exports = {
|
||||
// This plugins line is mandatory for the plugin to work with pnpm.
|
||||
// https://github.com/trivago/prettier-plugin-sort-imports/blob/61d069711008c530f5a41ca4e254781abc5de358/README.md?plain=1#L89-L96
|
||||
plugins: ['@trivago/prettier-plugin-sort-imports'],
|
||||
endOfLine: 'lf',
|
||||
semi: true,
|
||||
singleQuote: true,
|
||||
tabWidth: 2,
|
||||
trailingComma: 'es5',
|
||||
arrowParens: 'always',
|
||||
importOrder: ['^react$', '^(?!^react$|^@/|^[./]).*', '^@/(.*)$', '^[./]'],
|
||||
importOrderSeparation: true,
|
||||
importOrderSortSpecifiers: true,
|
||||
importOrderParserPlugins: ['jsx', 'typescript'],
|
||||
};
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
20
front/src/App.tsx
Normal file
20
front/src/App.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import { ErrorBoundary } from '@/errors/ErrorBoundary';
|
||||
|
||||
import { AudioPlayer } from './components';
|
||||
import { EnvDevelopment } from './components/EnvDevelopment/EnvDevelopment';
|
||||
import { AppRoutes } from './scene/AppRoutes';
|
||||
import { ServiceContextProvider } from './service/ServiceContext';
|
||||
|
||||
export const App = () => {
|
||||
return (
|
||||
<ServiceContextProvider>
|
||||
<EnvDevelopment />
|
||||
<ErrorBoundary>
|
||||
<AppRoutes />
|
||||
</ErrorBoundary>
|
||||
<AudioPlayer />
|
||||
</ServiceContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
@ -2,25 +2,75 @@
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 67.733333 67.733333"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.4 5da689c313, 2019-01-14"
|
||||
sodipodi:docname="ikon.svg"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
||||
sodipodi:docname="ikon_gray.svg"
|
||||
inkscape:export-filename="/home/heero/dev/perso/appl_pro/NoKomment/plugin/chrome/ikon.png"
|
||||
inkscape:export-xdpi="7.1250005"
|
||||
inkscape:export-ydpi="7.1250005">
|
||||
inkscape:export-ydpi="7.1250005"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs2" />
|
||||
id="defs2">
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB;"
|
||||
inkscape:label="Drop Shadow"
|
||||
id="filter5338"
|
||||
x="-0.12319682"
|
||||
y="-0.081815216"
|
||||
width="1.2463936"
|
||||
height="1.1636304">
|
||||
<feFlood
|
||||
flood-opacity="1"
|
||||
flood-color="rgb(255,255,255)"
|
||||
result="flood"
|
||||
id="feFlood5328" />
|
||||
<feComposite
|
||||
in="flood"
|
||||
in2="SourceGraphic"
|
||||
operator="in"
|
||||
result="composite1"
|
||||
id="feComposite5330" />
|
||||
<feGaussianBlur
|
||||
in="composite1"
|
||||
stdDeviation="2.1"
|
||||
result="blur"
|
||||
id="feGaussianBlur5332" />
|
||||
<feOffset
|
||||
dx="0"
|
||||
dy="0"
|
||||
result="offset"
|
||||
id="feOffset5334" />
|
||||
<feComposite
|
||||
in="SourceGraphic"
|
||||
in2="offset"
|
||||
operator="over"
|
||||
result="composite2"
|
||||
id="feComposite5336" />
|
||||
</filter>
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter1159"
|
||||
x="-0.11802406"
|
||||
width="1.2360481"
|
||||
y="-0.078379973"
|
||||
height="1.1567599">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="2.0118255"
|
||||
id="feGaussianBlur1161" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
@ -28,22 +78,31 @@
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.979899"
|
||||
inkscape:cx="52.480467"
|
||||
inkscape:cy="138.73493"
|
||||
inkscape:zoom="7.9195959"
|
||||
inkscape:cx="100.06824"
|
||||
inkscape:cy="115.66247"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:snap-text-baseline="false"
|
||||
inkscape:window-width="1918"
|
||||
inkscape:window-height="1038"
|
||||
inkscape:window-width="3838"
|
||||
inkscape:window-height="2118"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="20"
|
||||
inkscape:window-maximized="1">
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid4504" />
|
||||
id="grid4504"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingy="1"
|
||||
spacingx="1"
|
||||
units="px"
|
||||
visible="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
@ -61,18 +120,24 @@
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-229.26668)">
|
||||
transform="translate(0,-229.26668)"
|
||||
style="display:inline">
|
||||
<g
|
||||
aria-label="K"
|
||||
transform="scale(1.0347881,0.96638145)"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:84.55024719px;line-height:1.25;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.11376619"
|
||||
id="text821">
|
||||
id="text821-7"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:84.55024719px;line-height:1.25;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#2b3137;fill-opacity:1;stroke:none;stroke-width:2.11376619;stroke-opacity:1"
|
||||
transform="matrix(0.8407653,0,0,0.83753055,-37.28971,3.4402954)"
|
||||
aria-label="K">
|
||||
<path
|
||||
d="m 12.784421,241.62303 h 8.949095 v 27.37877 l 25.568842,-27.37877 6.39221,6.84469 -20.455074,21.90302 20.455074,27.37876 -6.39221,5.47576 -19.176632,-27.37877 -6.39221,6.84469 0,20.53408 h -8.949095 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:84.55024719px;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:2.11376619;fill:#ff0000;fill-opacity:1"
|
||||
id="path823"
|
||||
sodipodi:nodetypes="ccccccccccccccccccccccccccccccccccccssccccssscccccccccccccccsscccccsssccccccccccccccssscsscsss"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccccccccc" />
|
||||
id="path823-5"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:84.5502px;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;opacity:0.775;fill:#2b3137;fill-opacity:1;stroke-width:2.11377;filter:url(#filter5338)"
|
||||
d="m 65.200546,279.9533 h 8.949095 v 27.37877 l 25.568842,-27.37877 6.392207,6.84469 -20.455071,21.90302 20.455071,27.37876 -6.392207,5.47576 -19.176632,-27.37877 -6.39221,6.84469 v 20.53408 h -8.949095 z m 3.913007,39.48974 c -0.26846,-0.43226 -0.592093,-0.92734 -0.887692,-1.37494 l 0.02075,-0.022 c 0.456433,0.27687 0.977308,0.56258 1.422211,0.80755 l 0.407045,0.22999 -0.710959,0.75468 z m 0.591316,3.01367 0.778423,-0.82629 -0.642969,-1.02783 1.022328,-1.08519 1.052938,0.59264 0.80956,-0.85934 -4.631256,-2.27837 -0.913349,0.96952 z m 6.54394,-6.94635 0.762854,-0.80977 -1.289542,-1.22424 0.399591,-0.42416 1.938227,0.53566 0.856265,-0.90892 -2.195068,-0.54992 c 0.187965,-0.54159 0.09714,-1.11827 -0.429654,-1.61839 -0.850547,-0.80747 -1.705543,-0.42955 -2.421693,0.33064 l -1.198771,1.27249 z m -1.168715,-2.64352 -1.004195,-0.95334 0.373643,-0.39662 c 0.40478,-0.42967 0.738271,-0.54092 1.089465,-0.20751 0.351195,0.33341 0.31951,0.73118 -0.08527,1.16085 z m 7.416862,-3.98885 2.345648,-2.48989 -0.68044,-0.64598 -0.788801,0.83731 -2.216914,-2.10465 0.788802,-0.83731 -0.680439,-0.64598 -2.345647,2.48989 0.680439,0.64598 0.788802,-0.83731 2.216913,2.10465 -0.788801,0.83731 z m 6.206624,-6.58829 0.980813,-1.04113 c 0.939292,-0.99705 1.006613,-2.22712 -0.222565,-3.39405 -1.229181,-1.16694 -2.41593,-0.99961 -3.401932,0.047 l -0.934107,0.99155 z m 0.115038,-1.43521 -2.271787,-2.15674 0.124547,-0.13221 c 0.52932,-0.56189 1.150798,-0.69191 2.006834,0.12078 0.856035,0.81268 0.794297,1.47411 0.264954,2.03597 z m 6.226516,-5.29631 2.293753,-2.4348 -0.68044,-0.64599 -1.525707,1.61953 -0.823112,-0.78143 1.250665,-1.32757 -0.674951,-0.64077 -1.250666,1.32757 -0.71885,-0.68245 1.473813,-1.56444 -0.680438,-0.64598 -2.241858,2.37972 z m 7.372646,-7.6936 c 0.80437,-0.85384 0.71213,-2.05798 -0.51156,-3.2197 -1.19626,-1.13568 -2.393561,-1.15578 -3.197932,-0.30194 -0.80437,0.85383 -0.717618,2.05277 0.478638,3.18844 1.223694,1.16173 2.426484,1.18703 3.230854,0.3332 z m -0.6969,-0.66161 c -0.35289,0.37458 -0.97662,0.23116 -1.750342,-0.50339 -0.7408,-0.70328 -0.918234,-1.32045 -0.565349,-1.69503 0.352885,-0.37459 0.976611,-0.23116 1.717411,0.47213 0.77373,0.73454 0.95116,1.3517 0.59828,1.72629 z" />
|
||||
</g>
|
||||
<g
|
||||
id="text821"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:84.55024719px;line-height:1.25;font-family:'DejaVu Sans Mono';-inkscape-font-specification:'DejaVu Sans Mono, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#2b3137;fill-opacity:1;stroke:none;stroke-width:2.11376619;stroke-opacity:1;filter:url(#filter1159)"
|
||||
transform="matrix(1.0347881,0,0,0.96638144,-54.239583,-37.041665)"
|
||||
aria-label="K" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 7.5 KiB |
@ -10,7 +10,7 @@ import {
|
||||
} from "../rest-tools";
|
||||
|
||||
import {
|
||||
UUID,
|
||||
ObjectId,
|
||||
} from "../model";
|
||||
|
||||
export namespace DataResource {
|
||||
@ -30,13 +30,13 @@ export namespace DataResource {
|
||||
},
|
||||
params: {
|
||||
name: string,
|
||||
uuid: UUID,
|
||||
oid: ObjectId,
|
||||
},
|
||||
data: string,
|
||||
}): Promise<object> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/data/{uuid}/{name}",
|
||||
endPoint: "/data/{oid}/{name}",
|
||||
requestType: HTTPRequestModel.GET,
|
||||
},
|
||||
restConfig,
|
||||
@ -59,13 +59,13 @@ export namespace DataResource {
|
||||
Authorization?: string,
|
||||
},
|
||||
params: {
|
||||
uuid: UUID,
|
||||
oid: ObjectId,
|
||||
},
|
||||
data: string,
|
||||
}): Promise<object> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/data/{uuid}",
|
||||
endPoint: "/data/{oid}",
|
||||
requestType: HTTPRequestModel.GET,
|
||||
},
|
||||
restConfig,
|
||||
@ -88,13 +88,13 @@ export namespace DataResource {
|
||||
Authorization?: string,
|
||||
},
|
||||
params: {
|
||||
uuid: UUID,
|
||||
oid: ObjectId,
|
||||
},
|
||||
data: string,
|
||||
}): Promise<object> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/data/thumbnail/{uuid}",
|
||||
endPoint: "/data/thumbnail/{oid}",
|
||||
requestType: HTTPRequestModel.GET,
|
||||
},
|
||||
restConfig,
|
@ -154,17 +154,16 @@ export namespace MediaResource {
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
callback,
|
||||
callbacks,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
data: {
|
||||
fileName: string,
|
||||
file: File,
|
||||
},
|
||||
callback?: RESTCallbacks,
|
||||
callbacks?: RESTCallbacks,
|
||||
}): Promise<Media> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
@ -176,7 +175,7 @@ export namespace MediaResource {
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
callback,
|
||||
callbacks,
|
||||
}, isMedia);
|
||||
};
|
||||
/**
|
||||
@ -185,7 +184,7 @@ export namespace MediaResource {
|
||||
export function uploadFile({
|
||||
restConfig,
|
||||
data,
|
||||
callback,
|
||||
callbacks,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
data: {
|
||||
@ -198,7 +197,7 @@ export namespace MediaResource {
|
||||
typeId: string,
|
||||
title: string,
|
||||
},
|
||||
callback?: RESTCallbacks,
|
||||
callbacks?: RESTCallbacks,
|
||||
}): Promise<Media> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
@ -209,7 +208,7 @@ export namespace MediaResource {
|
||||
},
|
||||
restConfig,
|
||||
data,
|
||||
callback,
|
||||
callbacks,
|
||||
}, isMedia);
|
||||
};
|
||||
}
|
@ -176,17 +176,16 @@ export namespace SeasonResource {
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
callback,
|
||||
callbacks,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
data: {
|
||||
fileName: string,
|
||||
file: File,
|
||||
},
|
||||
callback?: RESTCallbacks,
|
||||
callbacks?: RESTCallbacks,
|
||||
}): Promise<Season> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
@ -198,7 +197,7 @@ export namespace SeasonResource {
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
callback,
|
||||
callbacks,
|
||||
}, isSeason);
|
||||
};
|
||||
}
|
@ -176,17 +176,16 @@ export namespace SeriesResource {
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
callback,
|
||||
callbacks,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
data: {
|
||||
fileName: string,
|
||||
file: File,
|
||||
},
|
||||
callback?: RESTCallbacks,
|
||||
callbacks?: RESTCallbacks,
|
||||
}): Promise<Series> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
@ -198,7 +197,7 @@ export namespace SeriesResource {
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
callback,
|
||||
callbacks,
|
||||
}, isSeries);
|
||||
};
|
||||
}
|
@ -176,17 +176,16 @@ export namespace TypeResource {
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
callback,
|
||||
callbacks,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
data: {
|
||||
fileName: string,
|
||||
file: File,
|
||||
},
|
||||
callback?: RESTCallbacks,
|
||||
callbacks?: RESTCallbacks,
|
||||
}): Promise<Type> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
@ -198,7 +197,7 @@ export namespace TypeResource {
|
||||
restConfig,
|
||||
params,
|
||||
data,
|
||||
callback,
|
||||
callbacks,
|
||||
}, isType);
|
||||
};
|
||||
}
|
67
front/src/back-api/model/data.ts
Normal file
67
front/src/back-api/model/data.ts
Normal file
@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodLong} from "./long";
|
||||
import {ZodObjectId} from "./object-id";
|
||||
import {ZodOIDGenericDataSoftDelete, ZodOIDGenericDataSoftDeleteWrite } from "./oid-generic-data-soft-delete";
|
||||
|
||||
export const ZodData = ZodOIDGenericDataSoftDelete.extend({
|
||||
/**
|
||||
* Sha512 of the data
|
||||
*/
|
||||
sha512: zod.string().max(512),
|
||||
/**
|
||||
* Mime -type of the media
|
||||
*/
|
||||
mimeType: zod.string().max(512),
|
||||
/**
|
||||
* Size in Byte of the data
|
||||
*/
|
||||
size: ZodLong,
|
||||
/**
|
||||
* Unique ObjectID of the object
|
||||
*/
|
||||
oid: ZodObjectId.readonly(),
|
||||
|
||||
});
|
||||
|
||||
export type Data = zod.infer<typeof ZodData>;
|
||||
|
||||
export function isData(data: any): data is Data {
|
||||
try {
|
||||
ZodData.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodData' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodDataWrite = ZodOIDGenericDataSoftDeleteWrite.extend({
|
||||
/**
|
||||
* Sha512 of the data
|
||||
*/
|
||||
sha512: zod.string().max(512).optional(),
|
||||
/**
|
||||
* Mime -type of the media
|
||||
*/
|
||||
mimeType: zod.string().max(512).optional(),
|
||||
/**
|
||||
* Size in Byte of the data
|
||||
*/
|
||||
size: ZodLong.optional(),
|
||||
|
||||
});
|
||||
|
||||
export type DataWrite = zod.infer<typeof ZodDataWrite>;
|
||||
|
||||
export function isDataWrite(data: any): data is DataWrite {
|
||||
try {
|
||||
ZodDataWrite.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodDataWrite' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodGenericData} from "./generic-data";
|
||||
import {ZodGenericData, ZodGenericDataWrite } from "./generic-data";
|
||||
|
||||
export const ZodGenericDataSoftDelete = ZodGenericData.extend({
|
||||
/**
|
||||
@ -24,14 +24,7 @@ export function isGenericDataSoftDelete(data: any): data is GenericDataSoftDelet
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const ZodGenericDataSoftDeleteWrite = ZodGenericDataSoftDelete.omit({
|
||||
deleted: true,
|
||||
id: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
|
||||
}).partial();
|
||||
export const ZodGenericDataSoftDeleteWrite = ZodGenericDataWrite;
|
||||
|
||||
export type GenericDataSoftDeleteWrite = zod.infer<typeof ZodGenericDataSoftDeleteWrite>;
|
||||
|
@ -4,7 +4,7 @@
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodLong} from "./long";
|
||||
import {ZodGenericTiming} from "./generic-timing";
|
||||
import {ZodGenericTiming, ZodGenericTimingWrite } from "./generic-timing";
|
||||
|
||||
export const ZodGenericData = ZodGenericTiming.extend({
|
||||
/**
|
||||
@ -25,13 +25,7 @@ export function isGenericData(data: any): data is GenericData {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const ZodGenericDataWrite = ZodGenericData.omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
|
||||
}).partial();
|
||||
export const ZodGenericDataWrite = ZodGenericTimingWrite;
|
||||
|
||||
export type GenericDataWrite = zod.infer<typeof ZodGenericDataWrite>;
|
||||
|
@ -28,12 +28,9 @@ export function isGenericTiming(data: any): data is GenericTiming {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodGenericTimingWrite = zod.object({
|
||||
|
||||
export const ZodGenericTimingWrite = ZodGenericTiming.omit({
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
|
||||
}).partial();
|
||||
});
|
||||
|
||||
export type GenericTimingWrite = zod.infer<typeof ZodGenericTimingWrite>;
|
||||
|
@ -19,8 +19,9 @@ export function isHealthResult(data: any): data is HealthResult {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodHealthResultWrite = zod.object({
|
||||
|
||||
export const ZodHealthResultWrite = ZodHealthResult.partial();
|
||||
});
|
||||
|
||||
export type HealthResultWrite = zod.infer<typeof ZodHealthResultWrite>;
|
||||
|
30
front/src/back-api/model/index.ts
Normal file
30
front/src/back-api/model/index.ts
Normal file
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
export * from "./data"
|
||||
export * from "./float"
|
||||
export * from "./generic-data"
|
||||
export * from "./generic-data-soft-delete"
|
||||
export * from "./generic-timing"
|
||||
export * from "./health-result"
|
||||
export * from "./integer"
|
||||
export * from "./iso-date"
|
||||
export * from "./jwt-header"
|
||||
export * from "./jwt-payload"
|
||||
export * from "./jwt-token"
|
||||
export * from "./long"
|
||||
export * from "./media"
|
||||
export * from "./media-informations-delta"
|
||||
export * from "./object-id"
|
||||
export * from "./oid-generic-data"
|
||||
export * from "./oid-generic-data-soft-delete"
|
||||
export * from "./rest-error-response"
|
||||
export * from "./season"
|
||||
export * from "./series"
|
||||
export * from "./timestamp"
|
||||
export * from "./type"
|
||||
export * from "./user"
|
||||
export * from "./user-karideo"
|
||||
export * from "./user-media-advancement"
|
||||
export * from "./user-out"
|
||||
export * from "./uuid"
|
40
front/src/back-api/model/jwt-header.ts
Normal file
40
front/src/back-api/model/jwt-header.ts
Normal file
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
|
||||
export const ZodJwtHeader = zod.object({
|
||||
typ: zod.string().max(128),
|
||||
alg: zod.string().max(128),
|
||||
|
||||
});
|
||||
|
||||
export type JwtHeader = zod.infer<typeof ZodJwtHeader>;
|
||||
|
||||
export function isJwtHeader(data: any): data is JwtHeader {
|
||||
try {
|
||||
ZodJwtHeader.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodJwtHeader' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodJwtHeaderWrite = zod.object({
|
||||
typ: zod.string().max(128).optional(),
|
||||
alg: zod.string().max(128).optional(),
|
||||
|
||||
});
|
||||
|
||||
export type JwtHeaderWrite = zod.infer<typeof ZodJwtHeaderWrite>;
|
||||
|
||||
export function isJwtHeaderWrite(data: any): data is JwtHeaderWrite {
|
||||
try {
|
||||
ZodJwtHeaderWrite.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodJwtHeaderWrite' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
51
front/src/back-api/model/jwt-payload.ts
Normal file
51
front/src/back-api/model/jwt-payload.ts
Normal file
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodLong} from "./long";
|
||||
|
||||
export const ZodJwtPayload = zod.object({
|
||||
sub: zod.string(),
|
||||
application: zod.string(),
|
||||
iss: zod.string(),
|
||||
right: zod.record(zod.string(), zod.record(zod.string(), ZodLong)),
|
||||
login: zod.string(),
|
||||
exp: ZodLong,
|
||||
iat: ZodLong,
|
||||
|
||||
});
|
||||
|
||||
export type JwtPayload = zod.infer<typeof ZodJwtPayload>;
|
||||
|
||||
export function isJwtPayload(data: any): data is JwtPayload {
|
||||
try {
|
||||
ZodJwtPayload.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodJwtPayload' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodJwtPayloadWrite = zod.object({
|
||||
sub: zod.string().optional(),
|
||||
application: zod.string().optional(),
|
||||
iss: zod.string().optional(),
|
||||
right: zod.record(zod.string(), zod.record(zod.string(), ZodLong)).optional(),
|
||||
login: zod.string().optional(),
|
||||
exp: ZodLong.optional(),
|
||||
iat: ZodLong.optional(),
|
||||
|
||||
});
|
||||
|
||||
export type JwtPayloadWrite = zod.infer<typeof ZodJwtPayloadWrite>;
|
||||
|
||||
export function isJwtPayloadWrite(data: any): data is JwtPayloadWrite {
|
||||
try {
|
||||
ZodJwtPayloadWrite.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodJwtPayloadWrite' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
44
front/src/back-api/model/jwt-token.ts
Normal file
44
front/src/back-api/model/jwt-token.ts
Normal file
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodJwtHeader, ZodJwtHeaderWrite } from "./jwt-header";
|
||||
import {ZodJwtPayload, ZodJwtPayloadWrite } from "./jwt-payload";
|
||||
|
||||
export const ZodJwtToken = zod.object({
|
||||
header: ZodJwtHeader,
|
||||
payload: ZodJwtPayload,
|
||||
signature: zod.string(),
|
||||
|
||||
});
|
||||
|
||||
export type JwtToken = zod.infer<typeof ZodJwtToken>;
|
||||
|
||||
export function isJwtToken(data: any): data is JwtToken {
|
||||
try {
|
||||
ZodJwtToken.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodJwtToken' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodJwtTokenWrite = zod.object({
|
||||
header: ZodJwtHeader.optional(),
|
||||
payload: ZodJwtPayload.optional(),
|
||||
signature: zod.string().optional(),
|
||||
|
||||
});
|
||||
|
||||
export type JwtTokenWrite = zod.infer<typeof ZodJwtTokenWrite>;
|
||||
|
||||
export function isJwtTokenWrite(data: any): data is JwtTokenWrite {
|
||||
try {
|
||||
ZodJwtTokenWrite.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodJwtTokenWrite' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
@ -19,8 +19,9 @@ export function isMediaInformationsDelta(data: any): data is MediaInformationsDe
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodMediaInformationsDeltaWrite = zod.object({
|
||||
|
||||
export const ZodMediaInformationsDeltaWrite = ZodMediaInformationsDelta.partial();
|
||||
});
|
||||
|
||||
export type MediaInformationsDeltaWrite = zod.infer<typeof ZodMediaInformationsDeltaWrite>;
|
||||
|
@ -3,10 +3,10 @@
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodUUID} from "./uuid";
|
||||
import {ZodObjectId} from "./object-id";
|
||||
import {ZodLong} from "./long";
|
||||
import {ZodInteger} from "./integer";
|
||||
import {ZodGenericDataSoftDelete} from "./generic-data-soft-delete";
|
||||
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteWrite } from "./generic-data-soft-delete";
|
||||
|
||||
export const ZodMedia = ZodGenericDataSoftDelete.extend({
|
||||
/**
|
||||
@ -20,7 +20,7 @@ export const ZodMedia = ZodGenericDataSoftDelete.extend({
|
||||
/**
|
||||
* Foreign Key Id of the data
|
||||
*/
|
||||
dataId: ZodUUID,
|
||||
dataId: ZodObjectId,
|
||||
/**
|
||||
* Type of the media
|
||||
*/
|
||||
@ -49,7 +49,7 @@ export const ZodMedia = ZodGenericDataSoftDelete.extend({
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodUUID),
|
||||
covers: zod.array(ZodObjectId).optional(),
|
||||
|
||||
});
|
||||
|
||||
@ -64,14 +64,50 @@ export function isMedia(data: any): data is Media {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodMediaWrite = ZodGenericDataSoftDeleteWrite.extend({
|
||||
/**
|
||||
* Name of the media (this represent the title)
|
||||
*/
|
||||
name: zod.string().optional(),
|
||||
/**
|
||||
* Description of the media
|
||||
*/
|
||||
description: zod.string().nullable().optional(),
|
||||
/**
|
||||
* Foreign Key Id of the data
|
||||
*/
|
||||
dataId: ZodObjectId.optional(),
|
||||
/**
|
||||
* Type of the media
|
||||
*/
|
||||
typeId: ZodLong.nullable().optional(),
|
||||
/**
|
||||
* Series reference of the media
|
||||
*/
|
||||
seriesId: ZodLong.nullable().optional(),
|
||||
/**
|
||||
* Season reference of the media
|
||||
*/
|
||||
seasonId: ZodLong.nullable().optional(),
|
||||
/**
|
||||
* Episode Id
|
||||
*/
|
||||
episode: ZodInteger.nullable().optional(),
|
||||
date: ZodInteger.nullable().optional(),
|
||||
/**
|
||||
* Creation years of the media
|
||||
*/
|
||||
time: ZodInteger.nullable().optional(),
|
||||
/**
|
||||
* Limitation Age of the media
|
||||
*/
|
||||
ageLimit: ZodInteger.nullable().optional(),
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodObjectId).nullable().optional(),
|
||||
|
||||
export const ZodMediaWrite = ZodMedia.omit({
|
||||
deleted: true,
|
||||
id: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
|
||||
}).partial();
|
||||
});
|
||||
|
||||
export type MediaWrite = zod.infer<typeof ZodMediaWrite>;
|
||||
|
8
front/src/back-api/model/object-id.ts
Normal file
8
front/src/back-api/model/object-id.ts
Normal file
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
|
||||
export const ZodObjectId = zod.string().length(24, "Invalid ObjectId length").regex(/^[a-fA-F0-9]{24}$/, "Invalid ObjectId format");
|
||||
export type ObjectId = zod.infer<typeof ZodObjectId>;
|
44
front/src/back-api/model/oid-generic-data-soft-delete.ts
Normal file
44
front/src/back-api/model/oid-generic-data-soft-delete.ts
Normal file
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodObjectId} from "./object-id";
|
||||
import {ZodOIDGenericData, ZodOIDGenericDataWrite } from "./oid-generic-data";
|
||||
|
||||
export const ZodOIDGenericDataSoftDelete = ZodOIDGenericData.extend({
|
||||
/**
|
||||
* Deleted state
|
||||
*/
|
||||
deleted: zod.boolean().readonly().optional(),
|
||||
/**
|
||||
* Unique ObjectID of the object
|
||||
*/
|
||||
oid: ZodObjectId.readonly(),
|
||||
|
||||
});
|
||||
|
||||
export type OIDGenericDataSoftDelete = zod.infer<typeof ZodOIDGenericDataSoftDelete>;
|
||||
|
||||
export function isOIDGenericDataSoftDelete(data: any): data is OIDGenericDataSoftDelete {
|
||||
try {
|
||||
ZodOIDGenericDataSoftDelete.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodOIDGenericDataSoftDelete' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodOIDGenericDataSoftDeleteWrite = ZodOIDGenericDataWrite;
|
||||
|
||||
export type OIDGenericDataSoftDeleteWrite = zod.infer<typeof ZodOIDGenericDataSoftDeleteWrite>;
|
||||
|
||||
export function isOIDGenericDataSoftDeleteWrite(data: any): data is OIDGenericDataSoftDeleteWrite {
|
||||
try {
|
||||
ZodOIDGenericDataSoftDeleteWrite.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodOIDGenericDataSoftDeleteWrite' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
40
front/src/back-api/model/oid-generic-data.ts
Normal file
40
front/src/back-api/model/oid-generic-data.ts
Normal file
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodObjectId} from "./object-id";
|
||||
import {ZodGenericTiming, ZodGenericTimingWrite } from "./generic-timing";
|
||||
|
||||
export const ZodOIDGenericData = ZodGenericTiming.extend({
|
||||
/**
|
||||
* Unique ObjectID of the object
|
||||
*/
|
||||
oid: ZodObjectId.readonly(),
|
||||
|
||||
});
|
||||
|
||||
export type OIDGenericData = zod.infer<typeof ZodOIDGenericData>;
|
||||
|
||||
export function isOIDGenericData(data: any): data is OIDGenericData {
|
||||
try {
|
||||
ZodOIDGenericData.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodOIDGenericData' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodOIDGenericDataWrite = ZodGenericTimingWrite;
|
||||
|
||||
export type OIDGenericDataWrite = zod.infer<typeof ZodOIDGenericDataWrite>;
|
||||
|
||||
export function isOIDGenericDataWrite(data: any): data is OIDGenericDataWrite {
|
||||
try {
|
||||
ZodOIDGenericDataWrite.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodOIDGenericDataWrite' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
24
front/src/back-api/model/part-right.ts
Normal file
24
front/src/back-api/model/part-right.ts
Normal file
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
|
||||
export enum PartRight {
|
||||
READ = 1,
|
||||
NONE = 0,
|
||||
WRITE = 2,
|
||||
READ_WRITE = 3,
|
||||
};
|
||||
|
||||
export const ZodPartRight = zod.nativeEnum(PartRight);
|
||||
|
||||
export function isPartRight(data: any): data is PartRight {
|
||||
try {
|
||||
ZodPartRight.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodPartRight' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
@ -3,15 +3,15 @@
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodUUID} from "./uuid";
|
||||
import {Zodint} from "./int";
|
||||
import {ZodObjectId} from "./object-id";
|
||||
import {ZodInteger} from "./integer";
|
||||
|
||||
export const ZodRestErrorResponse = zod.object({
|
||||
uuid: ZodUUID.optional(),
|
||||
oid: ZodObjectId.optional(),
|
||||
name: zod.string(),
|
||||
message: zod.string(),
|
||||
time: zod.string(),
|
||||
status: Zodint,
|
||||
status: ZodInteger,
|
||||
statusMessage: zod.string(),
|
||||
|
||||
});
|
||||
@ -27,17 +27,3 @@ export function isRestErrorResponse(data: any): data is RestErrorResponse {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const ZodRestErrorResponseWrite = ZodRestErrorResponse.partial();
|
||||
|
||||
export type RestErrorResponseWrite = zod.infer<typeof ZodRestErrorResponseWrite>;
|
||||
|
||||
export function isRestErrorResponseWrite(data: any): data is RestErrorResponseWrite {
|
||||
try {
|
||||
ZodRestErrorResponseWrite.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodRestErrorResponseWrite' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
@ -4,8 +4,8 @@
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodLong} from "./long";
|
||||
import {ZodUUID} from "./uuid";
|
||||
import {ZodGenericDataSoftDelete} from "./generic-data-soft-delete";
|
||||
import {ZodObjectId} from "./object-id";
|
||||
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteWrite } from "./generic-data-soft-delete";
|
||||
|
||||
export const ZodSeason = ZodGenericDataSoftDelete.extend({
|
||||
/**
|
||||
@ -23,7 +23,7 @@ export const ZodSeason = ZodGenericDataSoftDelete.extend({
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodUUID),
|
||||
covers: zod.array(ZodObjectId).optional(),
|
||||
|
||||
});
|
||||
|
||||
@ -38,14 +38,25 @@ export function isSeason(data: any): data is Season {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodSeasonWrite = ZodGenericDataSoftDeleteWrite.extend({
|
||||
/**
|
||||
* Name of the media (this represent the title)
|
||||
*/
|
||||
name: zod.string().optional(),
|
||||
/**
|
||||
* Description of the media
|
||||
*/
|
||||
description: zod.string().nullable().optional(),
|
||||
/**
|
||||
* series parent ID
|
||||
*/
|
||||
parentId: ZodLong.optional(),
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodObjectId).nullable().optional(),
|
||||
|
||||
export const ZodSeasonWrite = ZodSeason.omit({
|
||||
deleted: true,
|
||||
id: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
|
||||
}).partial();
|
||||
});
|
||||
|
||||
export type SeasonWrite = zod.infer<typeof ZodSeasonWrite>;
|
||||
|
@ -4,8 +4,8 @@
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodLong} from "./long";
|
||||
import {ZodUUID} from "./uuid";
|
||||
import {ZodGenericDataSoftDelete} from "./generic-data-soft-delete";
|
||||
import {ZodObjectId} from "./object-id";
|
||||
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteWrite } from "./generic-data-soft-delete";
|
||||
|
||||
export const ZodSeries = ZodGenericDataSoftDelete.extend({
|
||||
/**
|
||||
@ -23,7 +23,7 @@ export const ZodSeries = ZodGenericDataSoftDelete.extend({
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodUUID),
|
||||
covers: zod.array(ZodObjectId).optional(),
|
||||
|
||||
});
|
||||
|
||||
@ -38,14 +38,25 @@ export function isSeries(data: any): data is Series {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodSeriesWrite = ZodGenericDataSoftDeleteWrite.extend({
|
||||
/**
|
||||
* Name of the media (this represent the title)
|
||||
*/
|
||||
name: zod.string().optional(),
|
||||
/**
|
||||
* Description of the media
|
||||
*/
|
||||
description: zod.string().nullable().optional(),
|
||||
/**
|
||||
* series parent ID
|
||||
*/
|
||||
parentId: ZodLong.optional(),
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodObjectId).nullable().optional(),
|
||||
|
||||
export const ZodSeriesWrite = ZodSeries.omit({
|
||||
deleted: true,
|
||||
id: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
|
||||
}).partial();
|
||||
});
|
||||
|
||||
export type SeriesWrite = zod.infer<typeof ZodSeriesWrite>;
|
||||
|
@ -3,8 +3,8 @@
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodUUID} from "./uuid";
|
||||
import {ZodGenericDataSoftDelete} from "./generic-data-soft-delete";
|
||||
import {ZodObjectId} from "./object-id";
|
||||
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteWrite } from "./generic-data-soft-delete";
|
||||
|
||||
export const ZodType = ZodGenericDataSoftDelete.extend({
|
||||
/**
|
||||
@ -18,7 +18,7 @@ export const ZodType = ZodGenericDataSoftDelete.extend({
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodUUID),
|
||||
covers: zod.array(ZodObjectId).optional(),
|
||||
|
||||
});
|
||||
|
||||
@ -33,14 +33,21 @@ export function isType(data: any): data is Type {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodTypeWrite = ZodGenericDataSoftDeleteWrite.extend({
|
||||
/**
|
||||
* Name of the media (this represent the title)
|
||||
*/
|
||||
name: zod.string().optional(),
|
||||
/**
|
||||
* Description of the media
|
||||
*/
|
||||
description: zod.string().nullable().optional(),
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodObjectId).nullable().optional(),
|
||||
|
||||
export const ZodTypeWrite = ZodType.omit({
|
||||
deleted: true,
|
||||
id: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
|
||||
}).partial();
|
||||
});
|
||||
|
||||
export type TypeWrite = zod.infer<typeof ZodTypeWrite>;
|
||||
|
@ -3,11 +3,9 @@
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodUser} from "./user";
|
||||
import {ZodUser, ZodUserWrite } from "./user";
|
||||
|
||||
export const ZodUserKarideo = ZodUser.extend({
|
||||
|
||||
});
|
||||
export const ZodUserKarideo = ZodUser;
|
||||
|
||||
export type UserKarideo = zod.infer<typeof ZodUserKarideo>;
|
||||
|
||||
@ -20,14 +18,7 @@ export function isUserKarideo(data: any): data is UserKarideo {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const ZodUserKarideoWrite = ZodUserKarideo.omit({
|
||||
deleted: true,
|
||||
id: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
|
||||
}).partial();
|
||||
export const ZodUserKarideoWrite = ZodUserWrite;
|
||||
|
||||
export type UserKarideoWrite = zod.infer<typeof ZodUserKarideoWrite>;
|
||||
|
@ -6,13 +6,13 @@ import { z as zod } from "zod";
|
||||
import {ZodLong} from "./long";
|
||||
import {ZodFloat} from "./float";
|
||||
import {ZodInteger} from "./integer";
|
||||
import {ZodGenericDataSoftDelete} from "./generic-data-soft-delete";
|
||||
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteWrite } from "./generic-data-soft-delete";
|
||||
|
||||
export const ZodUserMediaAdvancement = ZodGenericDataSoftDelete.extend({
|
||||
/**
|
||||
* Foreign Key Id of the user
|
||||
*/
|
||||
userId: ZodLong,
|
||||
userId: ZodLong.optional(),
|
||||
/**
|
||||
* Id of the media
|
||||
*/
|
||||
@ -43,14 +43,29 @@ export function isUserMediaAdvancement(data: any): data is UserMediaAdvancement
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodUserMediaAdvancementWrite = ZodGenericDataSoftDeleteWrite.extend({
|
||||
/**
|
||||
* Foreign Key Id of the user
|
||||
*/
|
||||
userId: ZodLong.nullable().optional(),
|
||||
/**
|
||||
* Id of the media
|
||||
*/
|
||||
mediaId: ZodLong.optional(),
|
||||
/**
|
||||
* Percent of advancement in the media
|
||||
*/
|
||||
percent: ZodFloat.optional(),
|
||||
/**
|
||||
* Number of second of advancement in the media
|
||||
*/
|
||||
time: ZodInteger.optional(),
|
||||
/**
|
||||
* Number of time this media has been read
|
||||
*/
|
||||
count: ZodInteger.optional(),
|
||||
|
||||
export const ZodUserMediaAdvancementWrite = ZodUserMediaAdvancement.omit({
|
||||
deleted: true,
|
||||
id: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
|
||||
}).partial();
|
||||
});
|
||||
|
||||
export type UserMediaAdvancementWrite = zod.infer<typeof ZodUserMediaAdvancementWrite>;
|
||||
|
@ -7,7 +7,7 @@ import {ZodLong} from "./long";
|
||||
|
||||
export const ZodUserOut = zod.object({
|
||||
id: ZodLong,
|
||||
login: zod.string().max(255).optional(),
|
||||
login: zod.string().optional(),
|
||||
|
||||
});
|
||||
|
||||
@ -22,8 +22,11 @@ export function isUserOut(data: any): data is UserOut {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodUserOutWrite = zod.object({
|
||||
id: ZodLong,
|
||||
login: zod.string().nullable().optional(),
|
||||
|
||||
export const ZodUserOutWrite = ZodUserOut.partial();
|
||||
});
|
||||
|
||||
export type UserOutWrite = zod.infer<typeof ZodUserOutWrite>;
|
||||
|
@ -5,14 +5,13 @@ import { z as zod } from "zod";
|
||||
|
||||
import {ZodTimestamp} from "./timestamp";
|
||||
import {ZodUUID} from "./uuid";
|
||||
import {ZodGenericDataSoftDelete} from "./generic-data-soft-delete";
|
||||
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteWrite } from "./generic-data-soft-delete";
|
||||
|
||||
export const ZodUser = ZodGenericDataSoftDelete.extend({
|
||||
login: zod.string().max(128).optional(),
|
||||
login: zod.string().min(3).max(128),
|
||||
lastConnection: ZodTimestamp.optional(),
|
||||
admin: zod.boolean(),
|
||||
blocked: zod.boolean(),
|
||||
removed: zod.boolean(),
|
||||
blocked: zod.boolean().optional(),
|
||||
blockedReason: zod.string().max(512).optional(),
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
@ -31,14 +30,17 @@ export function isUser(data: any): data is User {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodUserWrite = ZodGenericDataSoftDeleteWrite.extend({
|
||||
login: zod.string().min(3).max(128).optional(),
|
||||
lastConnection: ZodTimestamp.nullable().optional(),
|
||||
blocked: zod.boolean().nullable().optional(),
|
||||
blockedReason: zod.string().max(512).nullable().optional(),
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodUUID).nullable().optional(),
|
||||
|
||||
export const ZodUserWrite = ZodUser.omit({
|
||||
deleted: true,
|
||||
id: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
|
||||
}).partial();
|
||||
});
|
||||
|
||||
export type UserWrite = zod.infer<typeof ZodUserWrite>;
|
||||
|
@ -7,11 +7,15 @@
|
||||
import { RestErrorResponse, isRestErrorResponse } from "./model";
|
||||
|
||||
export enum HTTPRequestModel {
|
||||
ARCHIVE = "ARCHIVE",
|
||||
DELETE = "DELETE",
|
||||
HEAD = "HEAD",
|
||||
GET = "GET",
|
||||
OPTION = "OPTION",
|
||||
PATCH = "PATCH",
|
||||
POST = "POST",
|
||||
PUT = "PUT",
|
||||
RESTORE = "RESTORE",
|
||||
}
|
||||
export enum HTTPMimeType {
|
||||
ALL = "*/*",
|
||||
@ -74,7 +78,7 @@ export interface RESTRequestType {
|
||||
data?: any;
|
||||
params?: object;
|
||||
queries?: object;
|
||||
callback?: RESTCallbacks;
|
||||
callbacks?: RESTCallbacks;
|
||||
}
|
||||
|
||||
function replaceAll(input, searchValue, replaceValue) {
|
||||
@ -237,7 +241,7 @@ export function RESTRequest({
|
||||
data,
|
||||
params,
|
||||
queries,
|
||||
callback,
|
||||
callbacks,
|
||||
}: RESTRequestType): Promise<ModelResponseHttp> {
|
||||
// Create the URL PATH:
|
||||
let generateUrl = RESTUrl({ restModel, restConfig, data, params, queries });
|
||||
@ -248,9 +252,14 @@ export function RESTRequest({
|
||||
if (restModel.accept !== undefined) {
|
||||
headers["Accept"] = restModel.accept;
|
||||
}
|
||||
if (restModel.requestType !== HTTPRequestModel.GET) {
|
||||
if (restModel.requestType !== HTTPRequestModel.GET &&
|
||||
restModel.requestType !== HTTPRequestModel.ARCHIVE &&
|
||||
restModel.requestType !== HTTPRequestModel.RESTORE
|
||||
) {
|
||||
// if Get we have not a content type, the body is empty
|
||||
if (restModel.contentType !== HTTPMimeType.MULTIPART) {
|
||||
if (restModel.contentType !== HTTPMimeType.MULTIPART &&
|
||||
restModel.contentType !== undefined
|
||||
) {
|
||||
// special case of multi-part ==> no content type otherwise the browser does not set the ";bundary=--****"
|
||||
headers["Content-Type"] = restModel.contentType;
|
||||
}
|
||||
@ -268,10 +277,10 @@ export function RESTRequest({
|
||||
return new Promise((resolve, reject) => {
|
||||
let action: undefined | Promise<Response> = undefined;
|
||||
if (
|
||||
isNullOrUndefined(callback) ||
|
||||
(isNullOrUndefined(callback.progressDownload) &&
|
||||
isNullOrUndefined(callback.progressUpload) &&
|
||||
isNullOrUndefined(callback.abortHandle))
|
||||
isNullOrUndefined(callbacks) ||
|
||||
(isNullOrUndefined(callbacks.progressDownload) &&
|
||||
isNullOrUndefined(callbacks.progressUpload) &&
|
||||
isNullOrUndefined(callbacks.abortHandle))
|
||||
) {
|
||||
// No information needed: call the generic fetch interface
|
||||
action = fetch(generateUrl, {
|
||||
@ -288,7 +297,7 @@ export function RESTRequest({
|
||||
headers,
|
||||
body,
|
||||
},
|
||||
callback
|
||||
callbacks
|
||||
);
|
||||
}
|
||||
action
|
374
front/src/components/AudioPlayer.tsx
Normal file
374
front/src/components/AudioPlayer.tsx
Normal file
@ -0,0 +1,374 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { Box, Flex, IconButton, SliderTrack, Text } from '@chakra-ui/react';
|
||||
import {
|
||||
MdFastForward,
|
||||
MdFastRewind,
|
||||
MdLooksOne,
|
||||
MdNavigateBefore,
|
||||
MdNavigateNext,
|
||||
MdPause,
|
||||
MdPlayArrow,
|
||||
MdRepeat,
|
||||
MdRepeatOne,
|
||||
MdStop,
|
||||
MdTrendingFlat,
|
||||
} from 'react-icons/md';
|
||||
|
||||
import { useColorModeValue } from '@/components/ui/color-mode';
|
||||
import { useActivePlaylistService } from '@/service/ActivePlaylist';
|
||||
import { useSpecificMedia } from '@/service/Media';
|
||||
import { useSpecificSeason } from '@/service/Season';
|
||||
import { useSpecificSeries } from '@/service/Series';
|
||||
import { useSpecificType } from '@/service/Type';
|
||||
import { DataUrlAccess } from '@/utils/data-url-access';
|
||||
import { isNullOrUndefined } from '@/utils/validator';
|
||||
|
||||
import { Slider } from './ui/slider';
|
||||
|
||||
export enum PlayMode {
|
||||
PLAY_ONE,
|
||||
PLAY_ALL,
|
||||
PLAY_ONE_LOOP,
|
||||
PLAY_ALL_LOOP,
|
||||
}
|
||||
|
||||
const playModeIcon = {
|
||||
[PlayMode.PLAY_ONE]: <MdLooksOne style={{ width: '100%', height: '100%' }} />,
|
||||
[PlayMode.PLAY_ALL]: (
|
||||
<MdTrendingFlat style={{ width: '100%', height: '100%' }} />
|
||||
),
|
||||
[PlayMode.PLAY_ONE_LOOP]: (
|
||||
<MdRepeatOne style={{ width: '100%', height: '100%' }} />
|
||||
),
|
||||
[PlayMode.PLAY_ALL_LOOP]: (
|
||||
<MdRepeat style={{ width: '100%', height: '100%' }} />
|
||||
),
|
||||
};
|
||||
|
||||
export type AudioPlayerProps = {};
|
||||
|
||||
const formatTime = (time) => {
|
||||
if (time && !isNaN(time)) {
|
||||
const minutes = Math.floor(time / 60);
|
||||
const formatMinutes = minutes < 10 ? `0${minutes}` : `${minutes}`;
|
||||
const seconds = Math.floor(time % 60);
|
||||
const formatSeconds = seconds < 10 ? `0${seconds}` : `${seconds}`;
|
||||
return `${formatMinutes}:${formatSeconds}`;
|
||||
}
|
||||
return '00:00';
|
||||
};
|
||||
|
||||
export const AudioPlayer = ({}: AudioPlayerProps) => {
|
||||
const { playMediaList, MediaOffset, previous, next, first } =
|
||||
useActivePlaylistService();
|
||||
const audioRef = useRef<HTMLAudioElement>(null);
|
||||
const [isPlaying, setIsPlaying] = useState<boolean>(false);
|
||||
const [timeProgress, setTimeProgress] = useState<number>(0);
|
||||
const [playingMode, setPlayingMode] = useState<PlayMode>(PlayMode.PLAY_ALL);
|
||||
const [duration, setDuration] = useState<number>(0);
|
||||
const { dataMedia } = useSpecificMedia(
|
||||
MediaOffset !== undefined ? playMediaList[MediaOffset] : undefined
|
||||
);
|
||||
const { dataSeason } = useSpecificSeason(dataMedia?.seasonId);
|
||||
const { dataType } = useSpecificType(dataMedia?.typeId);
|
||||
const { dataSeries } = useSpecificSeries(dataMedia?.seriesId);
|
||||
|
||||
const [mediaSource, setMediaSource] = useState<string>('');
|
||||
useEffect(() => {
|
||||
setMediaSource(
|
||||
dataMedia && dataMedia?.dataId
|
||||
? DataUrlAccess.getUrl(dataMedia?.dataId)
|
||||
: ''
|
||||
);
|
||||
}, [dataMedia, setMediaSource]);
|
||||
const backColor = useColorModeValue('back.100', 'back.800');
|
||||
const configButton = {
|
||||
borderRadius: 'full',
|
||||
backgroundColor: 'transparent',
|
||||
_hover: {
|
||||
bgColor: 'brand.500',
|
||||
},
|
||||
width: '50px',
|
||||
height: '50px',
|
||||
padding: '5px',
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!audioRef || !audioRef.current) {
|
||||
return;
|
||||
}
|
||||
if (isPlaying) {
|
||||
audioRef.current.play();
|
||||
} else {
|
||||
audioRef.current.pause();
|
||||
}
|
||||
}, [isPlaying, audioRef]);
|
||||
|
||||
const onAudioEnded = () => {
|
||||
if (playMediaList.length === 0 || isNullOrUndefined(MediaOffset)) {
|
||||
return;
|
||||
}
|
||||
if (playingMode === PlayMode.PLAY_ALL_LOOP) {
|
||||
if (playMediaList.length == MediaOffset + 1) {
|
||||
first();
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
} else if (playingMode === PlayMode.PLAY_ALL) {
|
||||
next();
|
||||
} else if (playingMode === PlayMode.PLAY_ONE_LOOP) {
|
||||
onSeek(0);
|
||||
onPlay();
|
||||
}
|
||||
};
|
||||
const onSeek = (newValue) => {
|
||||
console.log(`onSeek: ${newValue}`);
|
||||
if (!audioRef || !audioRef.current) {
|
||||
return;
|
||||
}
|
||||
audioRef.current.currentTime = newValue;
|
||||
};
|
||||
const onPlay = () => {
|
||||
if (!audioRef || !audioRef.current) {
|
||||
return;
|
||||
}
|
||||
if (isPlaying) {
|
||||
audioRef.current.pause();
|
||||
} else {
|
||||
audioRef.current.play();
|
||||
}
|
||||
};
|
||||
const onStop = () => {
|
||||
if (!audioRef || !audioRef.current) {
|
||||
return;
|
||||
}
|
||||
if (audioRef.current.currentTime == 0 && audioRef.current.paused) {
|
||||
// TODO remove current playing value
|
||||
} else {
|
||||
audioRef.current.pause();
|
||||
audioRef.current.currentTime = 0;
|
||||
}
|
||||
};
|
||||
const onNavigatePrevious = () => {
|
||||
previous();
|
||||
};
|
||||
const onFastRewind = () => {
|
||||
if (!audioRef || !audioRef.current) {
|
||||
return;
|
||||
}
|
||||
audioRef.current.currentTime -= 10;
|
||||
};
|
||||
const onFastForward = () => {
|
||||
if (!audioRef || !audioRef.current) {
|
||||
return;
|
||||
}
|
||||
audioRef.current.currentTime += 10;
|
||||
};
|
||||
const onNavigateNext = () => {
|
||||
next();
|
||||
};
|
||||
const onTypePlay = () => {
|
||||
setPlayingMode((value: PlayMode) => {
|
||||
if (value === PlayMode.PLAY_ONE) {
|
||||
return PlayMode.PLAY_ALL;
|
||||
} else if (value === PlayMode.PLAY_ALL) {
|
||||
return PlayMode.PLAY_ONE_LOOP;
|
||||
} else if (value === PlayMode.PLAY_ONE_LOOP) {
|
||||
return PlayMode.PLAY_ALL_LOOP;
|
||||
} else {
|
||||
return PlayMode.PLAY_ONE;
|
||||
}
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Call when meta-data is updated
|
||||
*/
|
||||
function onChangeMetadata(): void {
|
||||
const seconds = audioRef.current?.duration;
|
||||
if (seconds !== undefined) {
|
||||
setDuration(seconds);
|
||||
}
|
||||
}
|
||||
const onTimeUpdate = () => {
|
||||
if (!audioRef || !audioRef.current) {
|
||||
return;
|
||||
}
|
||||
console.log(`onTimeUpdate ${audioRef.current.currentTime}`);
|
||||
setTimeProgress(audioRef.current.currentTime);
|
||||
};
|
||||
const onDurationChange = (event) => {};
|
||||
const onChangeStateToPlay = () => {
|
||||
setIsPlaying(true);
|
||||
};
|
||||
const onChangeStateToPause = () => {
|
||||
setIsPlaying(false);
|
||||
};
|
||||
const marks = () => {
|
||||
const minutes = Math.floor(duration / 60);
|
||||
const result: number[] = [];
|
||||
for (let i = 1; i <= minutes; i++) {
|
||||
result.push(60 * i);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return (
|
||||
<>
|
||||
{!isNullOrUndefined(MediaOffset) && (
|
||||
<Flex
|
||||
position="absolute"
|
||||
height="150px"
|
||||
minHeight="150px"
|
||||
paddingY="5px"
|
||||
paddingX="10px"
|
||||
marginX="15px"
|
||||
bottom={0}
|
||||
left={0}
|
||||
right={0}
|
||||
zIndex={1000}
|
||||
borderWidth="1px"
|
||||
borderColor="brand.900"
|
||||
bgColor={backColor}
|
||||
borderTopRadius="10px"
|
||||
direction="column"
|
||||
>
|
||||
<Text
|
||||
alignContent="left"
|
||||
fontSize="20px"
|
||||
fontWeight="bold"
|
||||
userSelect="none"
|
||||
marginRight="auto"
|
||||
overflow="hidden"
|
||||
// noOfLines={1}
|
||||
>
|
||||
{dataMedia?.name ?? '???'}
|
||||
</Text>
|
||||
<Text
|
||||
alignContent="left"
|
||||
fontSize="16px"
|
||||
userSelect="none"
|
||||
marginRight="auto"
|
||||
overflow="hidden"
|
||||
// noOfLines={1}
|
||||
>
|
||||
{dataSeries && dataSeries.name}
|
||||
{dataSeason && dataSeason?.name}
|
||||
{dataType && ` / ${dataType.name}`}
|
||||
</Text>
|
||||
<Box width="full" paddingX="15px">
|
||||
<Slider
|
||||
defaultValue={[0]}
|
||||
value={[timeProgress]}
|
||||
min={0}
|
||||
max={duration}
|
||||
step={0.1}
|
||||
onValueChange={(e) => onSeek(e.value)}
|
||||
variant="outline"
|
||||
colorPalette="brand"
|
||||
marks={marks()}
|
||||
//focusCapture={false}
|
||||
>
|
||||
<SliderTrack
|
||||
bg="brand.200"
|
||||
height="10px"
|
||||
borderRadius="full"
|
||||
></SliderTrack>
|
||||
</Slider>
|
||||
</Box>
|
||||
<Flex>
|
||||
<Text
|
||||
alignContent="left"
|
||||
fontSize="16px"
|
||||
userSelect="none"
|
||||
marginRight="auto"
|
||||
overflow="hidden"
|
||||
// noOfLines={1}
|
||||
>
|
||||
{formatTime(timeProgress)}
|
||||
</Text>
|
||||
<Text alignContent="left" fontSize="16px" userSelect="none">
|
||||
{formatTime(duration)}
|
||||
</Text>
|
||||
</Flex>
|
||||
<Flex gap="5px">
|
||||
<IconButton
|
||||
{...configButton}
|
||||
aria-label={'Play'}
|
||||
onClick={onPlay}
|
||||
variant="ghost"
|
||||
>
|
||||
{isPlaying ? (
|
||||
<MdPause style={{ width: '100%', height: '100%' }} />
|
||||
) : (
|
||||
<MdPlayArrow style={{ width: '100%', height: '100%' }} />
|
||||
)}
|
||||
</IconButton>
|
||||
<IconButton
|
||||
{...configButton}
|
||||
aria-label={'Stop'}
|
||||
onClick={onStop}
|
||||
variant="ghost"
|
||||
>
|
||||
<MdStop style={{ width: '100%', height: '100%' }} />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
{...configButton}
|
||||
aria-label={'Previous Media'}
|
||||
onClick={onNavigatePrevious}
|
||||
marginLeft="auto"
|
||||
variant="ghost"
|
||||
>
|
||||
<MdNavigateBefore style={{ width: '100%', height: '100%' }} />{' '}
|
||||
</IconButton>
|
||||
<IconButton
|
||||
{...configButton}
|
||||
aria-label={'jump 15sec in past'}
|
||||
onClick={onFastRewind}
|
||||
variant="ghost"
|
||||
>
|
||||
<MdFastRewind style={{ width: '100%', height: '100%' }} />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
{...configButton}
|
||||
aria-label={'jump 15sec in future'}
|
||||
onClick={onFastForward}
|
||||
variant="ghost"
|
||||
>
|
||||
<MdFastForward style={{ width: '100%', height: '100%' }} />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
{...configButton}
|
||||
aria-label={'Next Media'}
|
||||
marginRight="auto"
|
||||
onClick={onNavigateNext}
|
||||
variant="ghost"
|
||||
>
|
||||
<MdNavigateNext style={{ width: '100%', height: '100%' }} />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
{...configButton}
|
||||
aria-label={'continue to the end'}
|
||||
onClick={onTypePlay}
|
||||
variant="ghost"
|
||||
>
|
||||
{playModeIcon[playingMode]}
|
||||
</IconButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
<audio
|
||||
src={mediaSource}
|
||||
ref={audioRef}
|
||||
//preload={true}
|
||||
onPlay={onChangeStateToPlay}
|
||||
onPause={onChangeStateToPause}
|
||||
onTimeUpdate={onTimeUpdate}
|
||||
onDurationChange={onDurationChange}
|
||||
onLoadedMetadata={onChangeMetadata}
|
||||
autoPlay={true}
|
||||
onEnded={onAudioEnded}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
112
front/src/components/Cover.tsx
Normal file
112
front/src/components/Cover.tsx
Normal file
@ -0,0 +1,112 @@
|
||||
import { ReactElement, useEffect, useState } from 'react';
|
||||
|
||||
import { Box, BoxProps, Flex, FlexProps } from '@chakra-ui/react';
|
||||
import { Image } from '@chakra-ui/react';
|
||||
|
||||
import { ObjectId } from '@/back-api';
|
||||
import { DataUrlAccess } from '@/utils/data-url-access';
|
||||
|
||||
import { Icon } from './Icon';
|
||||
|
||||
export type CoversProps = Omit<BoxProps, 'iconEmpty'> & {
|
||||
data?: ObjectId[];
|
||||
size?: BoxProps['width'];
|
||||
iconEmpty?: ReactElement;
|
||||
slideshow?: boolean;
|
||||
};
|
||||
|
||||
export const Covers = ({
|
||||
data,
|
||||
iconEmpty,
|
||||
size = '100px',
|
||||
slideshow = false,
|
||||
...rest
|
||||
}: CoversProps) => {
|
||||
const [currentImageIndex, setCurrentImageIndex] = useState(0);
|
||||
const [previousImageIndex, setPreviousImageIndex] = useState(0);
|
||||
const [topOpacity, setTopOpacity] = useState(0.0);
|
||||
|
||||
useEffect(() => {
|
||||
if (!slideshow) {
|
||||
return;
|
||||
}
|
||||
const interval = setInterval(() => {
|
||||
setPreviousImageIndex(currentImageIndex);
|
||||
setTopOpacity(0.0);
|
||||
setTimeout(() => {
|
||||
setCurrentImageIndex(
|
||||
(prevIndex) => (prevIndex + 1) % (data?.length ?? 1)
|
||||
);
|
||||
setTopOpacity(1.0);
|
||||
}, 1500);
|
||||
}, 3000);
|
||||
return () => clearInterval(interval);
|
||||
}, [slideshow, data]);
|
||||
|
||||
if (!data || data.length < 1) {
|
||||
if (iconEmpty) {
|
||||
return <Icon children={iconEmpty} sizeIcon={size} />;
|
||||
} else {
|
||||
return (
|
||||
<Box
|
||||
width={size}
|
||||
height={size}
|
||||
minHeight={size}
|
||||
minWidth={size}
|
||||
borderColor="blue"
|
||||
borderWidth="1px"
|
||||
margin="auto"
|
||||
{...rest}
|
||||
></Box>
|
||||
);
|
||||
}
|
||||
}
|
||||
if (slideshow === false || data.length === 1) {
|
||||
const url = DataUrlAccess.getThumbnailUrl(data[0]);
|
||||
return (
|
||||
<Image
|
||||
loading="lazy"
|
||||
src={url}
|
||||
maxWidth={size}
|
||||
boxSize={size} /*{...rest}*/
|
||||
/>
|
||||
);
|
||||
}
|
||||
const urlCurrent = DataUrlAccess.getThumbnailUrl(data[currentImageIndex]);
|
||||
const urlPrevious = DataUrlAccess.getThumbnailUrl(data[previousImageIndex]);
|
||||
return (
|
||||
<Flex
|
||||
position="relative"
|
||||
// {...rest}
|
||||
maxWidth={size}
|
||||
width={size}
|
||||
height={size}
|
||||
overflow="hidden"
|
||||
>
|
||||
<Image
|
||||
src={urlPrevious}
|
||||
loading="lazy"
|
||||
position="absolute"
|
||||
top="0"
|
||||
left="0"
|
||||
width="100%"
|
||||
height="100%"
|
||||
zIndex={1}
|
||||
boxSize={size}
|
||||
/>
|
||||
<Image
|
||||
src={urlCurrent}
|
||||
loading="lazy"
|
||||
position="absolute"
|
||||
top="0"
|
||||
left="0"
|
||||
width="100%"
|
||||
height="100%"
|
||||
boxSize={size}
|
||||
transition="opacity 0.5s ease-in-out"
|
||||
opacity={topOpacity}
|
||||
zIndex={2}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
};
|
13
front/src/components/EmptyEnd.tsx
Normal file
13
front/src/components/EmptyEnd.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import { Box } from '@chakra-ui/react';
|
||||
|
||||
export const EmptyEnd = () => {
|
||||
return (
|
||||
<Box
|
||||
width="full"
|
||||
height="25%"
|
||||
minHeight="250px"
|
||||
// borderWidth="1px"
|
||||
// borderColor="red"
|
||||
></Box>
|
||||
);
|
||||
};
|
117
front/src/components/EnvDevelopment/EnvDevelopment.tsx
Normal file
117
front/src/components/EnvDevelopment/EnvDevelopment.tsx
Normal file
@ -0,0 +1,117 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Dialog,
|
||||
Select,
|
||||
Stack,
|
||||
Text,
|
||||
createListCollection,
|
||||
useDisclosure,
|
||||
} from '@chakra-ui/react';
|
||||
|
||||
import { environment } from '@/environment';
|
||||
import { USERS } from '@/service/session';
|
||||
import { hashLocalData } from '@/utils/sso';
|
||||
|
||||
export const USERS_COLLECTION = createListCollection({
|
||||
items: [
|
||||
{ label: 'karadmin', value: 'adminA@666' },
|
||||
{ label: 'karuser', value: 'userA@666' },
|
||||
{ label: 'NO_USER', value: '' },
|
||||
],
|
||||
});
|
||||
|
||||
export const EnvDevelopment = () => {
|
||||
const dialog = useDisclosure();
|
||||
const [selectUserTest, setSelectUserTest] = useState<string>('NO_USER');
|
||||
//const setUser = useRightsStore((store) => store.setUser);
|
||||
const buildEnv =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? 'Development'
|
||||
: import.meta.env.VITE_DEV_ENV_NAME;
|
||||
const envName: Array<string> = [];
|
||||
!!buildEnv && envName.push(buildEnv);
|
||||
if (!envName.length) {
|
||||
return null;
|
||||
}
|
||||
const handleChange = (selectedOption) => {
|
||||
console.log(`SELECT: [${selectedOption.target.value}]`);
|
||||
setSelectUserTest(selectedOption.target.value);
|
||||
};
|
||||
const onClose = () => {
|
||||
dialog.onClose();
|
||||
if (selectUserTest == 'NO_USER') {
|
||||
window.location.href = `/${environment.applName}/sso/${hashLocalData()}/false/__LOGOUT__`;
|
||||
} else {
|
||||
window.location.href = `/${environment.applName}/sso/${hashLocalData()}/true/${USERS[selectUserTest]}`;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
as="button"
|
||||
zIndex="100000"
|
||||
position="fixed"
|
||||
top="0"
|
||||
insetStart="0"
|
||||
insetEnd="0"
|
||||
h="2px"
|
||||
bg="warning.400"
|
||||
cursor="pointer"
|
||||
data-test-id="devtools"
|
||||
onClick={dialog.onOpen}
|
||||
>
|
||||
<Text
|
||||
position="fixed"
|
||||
top="0"
|
||||
insetStart="4"
|
||||
bg="warning.400"
|
||||
color="warning.900"
|
||||
fontSize="0.6rem"
|
||||
fontWeight="bold"
|
||||
px="10px"
|
||||
marginLeft="25%"
|
||||
borderBottomStartRadius="sm"
|
||||
borderBottomEndRadius="sm"
|
||||
textTransform="uppercase"
|
||||
>
|
||||
{envName.join(' : ')}
|
||||
</Text>
|
||||
</Box>
|
||||
<Dialog.Root open={dialog.open} onOpenChange={dialog.onClose}>
|
||||
<Dialog.Positioner>
|
||||
<Dialog.Backdrop />
|
||||
<Dialog.Content>
|
||||
<Dialog.Header>Outils développeurs</Dialog.Header>
|
||||
<Dialog.Body>
|
||||
<Stack>
|
||||
<Text>User</Text>
|
||||
<Select.Root
|
||||
onChange={handleChange}
|
||||
collection={USERS_COLLECTION}
|
||||
>
|
||||
<Select.Trigger>
|
||||
<Select.ValueText placeholder="Select test user" />
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{USERS_COLLECTION.items.map((value) => (
|
||||
<Select.Item item={value} key={value.value}>
|
||||
{value.label}
|
||||
</Select.Item>
|
||||
))}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</Stack>
|
||||
</Dialog.Body>
|
||||
<Dialog.Footer>
|
||||
<Button onClick={onClose}>Close</Button>
|
||||
</Dialog.Footer>
|
||||
</Dialog.Content>
|
||||
</Dialog.Positioner>
|
||||
</Dialog.Root>
|
||||
</>
|
||||
);
|
||||
};
|
40
front/src/components/Icon.tsx
Normal file
40
front/src/components/Icon.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import { ReactNode, forwardRef } from 'react';
|
||||
|
||||
import { Box, Flex, FlexProps } from '@chakra-ui/react';
|
||||
|
||||
export type IconProps = FlexProps & {
|
||||
children: ReactNode;
|
||||
color?: string;
|
||||
sizeIcon?: FlexProps['width'];
|
||||
};
|
||||
|
||||
export const Icon = forwardRef<HTMLDivElement, IconProps>(
|
||||
({ children, color, sizeIcon = '1em', ...rest }, ref) => {
|
||||
return (
|
||||
<Flex
|
||||
flex="none"
|
||||
minWidth={sizeIcon}
|
||||
minHeight={sizeIcon}
|
||||
maxWidth={sizeIcon}
|
||||
maxHeight={sizeIcon}
|
||||
align="center"
|
||||
padding="1px"
|
||||
ref={ref}
|
||||
{...rest}
|
||||
>
|
||||
<Box
|
||||
marginX="auto"
|
||||
width="100%"
|
||||
minWidth="100%"
|
||||
height="100%"
|
||||
color={color}
|
||||
asChild
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Icon.displayName = 'Icon';
|
52
front/src/components/Layout/PageLayout.tsx
Normal file
52
front/src/components/Layout/PageLayout.tsx
Normal file
@ -0,0 +1,52 @@
|
||||
import React, { ReactNode, useEffect } from 'react';
|
||||
|
||||
import { Flex, Image } from '@chakra-ui/react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
import ikon from '@/assets/images/ikon.svg';
|
||||
import { TOP_BAR_HEIGHT } from '@/components/TopBar/TopBar';
|
||||
|
||||
export type LayoutProps = React.PropsWithChildren<unknown> & {
|
||||
topBar?: ReactNode;
|
||||
};
|
||||
|
||||
export const PageLayout = ({ children }: LayoutProps) => {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, [pathname]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex
|
||||
minH={`calc(100vh - ${TOP_BAR_HEIGHT})`}
|
||||
maxH={`calc(100vh - ${TOP_BAR_HEIGHT})`}
|
||||
position="absolute"
|
||||
top={TOP_BAR_HEIGHT}
|
||||
bottom={0}
|
||||
left={0}
|
||||
right={0}
|
||||
minWidth="300px"
|
||||
zIndex={0}
|
||||
>
|
||||
<Image src={ikon} boxSize="90vh" margin="auto" />
|
||||
</Flex>
|
||||
<Flex
|
||||
direction="column"
|
||||
overflowX="auto"
|
||||
overflowY="auto"
|
||||
minH={`calc(100vh - ${TOP_BAR_HEIGHT})`}
|
||||
maxH={`calc(100vh - ${TOP_BAR_HEIGHT})`}
|
||||
position="absolute"
|
||||
top={TOP_BAR_HEIGHT}
|
||||
bottom={0}
|
||||
left={0}
|
||||
right={0}
|
||||
minWidth="300px"
|
||||
>
|
||||
{children}
|
||||
</Flex>
|
||||
</>
|
||||
);
|
||||
};
|
43
front/src/components/Layout/PageLayoutInfoCenter.tsx
Normal file
43
front/src/components/Layout/PageLayoutInfoCenter.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
import { ReactNode, useEffect } from 'react';
|
||||
|
||||
import { Flex, FlexProps } from '@chakra-ui/react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
import { PageLayout } from '@/components/Layout/PageLayout';
|
||||
import { useColorModeValue } from '@/components/ui/color-mode';
|
||||
import { colors } from '@/theme/colors';
|
||||
|
||||
export type LayoutProps = FlexProps & {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export const PageLayoutInfoCenter = ({
|
||||
children,
|
||||
width = '25%',
|
||||
...rest
|
||||
}: LayoutProps) => {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, [pathname]);
|
||||
|
||||
return (
|
||||
<PageLayout>
|
||||
<Flex
|
||||
direction="column"
|
||||
margin="auto"
|
||||
minWidth={width}
|
||||
border="back.900"
|
||||
borderWidth="1px"
|
||||
borderRadius="8px"
|
||||
padding="10px"
|
||||
boxShadow={'0px 0px 16px ' + colors.back[900]}
|
||||
backgroundColor={useColorModeValue('#FFFFFF', '#000000')}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</Flex>
|
||||
</PageLayout>
|
||||
);
|
||||
};
|
20
front/src/components/ParameterLayout/ParameterLayout.ts
Normal file
20
front/src/components/ParameterLayout/ParameterLayout.ts
Normal file
@ -0,0 +1,20 @@
|
||||
export {
|
||||
ParameterLayoutContent as Content,
|
||||
type ParameterLayoutContentProps as ContentProps,
|
||||
} from './ParameterLayoutContent';
|
||||
export {
|
||||
ParameterLayoutFooter as Footer,
|
||||
type ParameterLayoutFooterProps as FooterProps,
|
||||
} from './ParameterLayoutFooter';
|
||||
export {
|
||||
ParameterLayoutHeader as Header,
|
||||
type ParameterLayoutHeaderProps as HeaderProps,
|
||||
} from './ParameterLayoutHeader';
|
||||
export {
|
||||
ParameterLayoutHeaderBase as HeaderBase,
|
||||
type ParameterLayoutHeaderBaseProps as HeaderBaseProps,
|
||||
} from './ParameterLayoutHeaderBase';
|
||||
export {
|
||||
ParameterLayoutRoot as Root,
|
||||
type ParameterLayoutRootProps as RootProps,
|
||||
} from './ParameterLayoutRoot';
|
@ -0,0 +1,25 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
|
||||
export type ParameterLayoutContentProps = {
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
export const ParameterLayoutContent = ({
|
||||
children,
|
||||
}: ParameterLayoutContentProps) => {
|
||||
return (
|
||||
<Flex
|
||||
direction="column"
|
||||
width="full"
|
||||
borderY="1px solid black"
|
||||
paddingY="15px"
|
||||
paddingX="25px"
|
||||
minHeight="10px"
|
||||
background="gray.700"
|
||||
>
|
||||
{children}
|
||||
</Flex>
|
||||
);
|
||||
};
|
@ -0,0 +1,17 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
|
||||
export type ParameterLayoutFooterProps = {
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
export const ParameterLayoutFooter = ({
|
||||
children,
|
||||
}: ParameterLayoutFooterProps) => {
|
||||
return (
|
||||
<Flex width="full" paddingY="15px" paddingX="25px" minHeight="10px">
|
||||
{children}
|
||||
</Flex>
|
||||
);
|
||||
};
|
@ -0,0 +1,17 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
|
||||
export type ParameterLayoutHeaderProps = {
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
export const ParameterLayoutHeader = ({
|
||||
children,
|
||||
}: ParameterLayoutHeaderProps) => {
|
||||
return (
|
||||
<Flex width="full" paddingY="15px" paddingX="25px" minHeight="10px">
|
||||
{children}
|
||||
</Flex>
|
||||
);
|
||||
};
|
@ -0,0 +1,24 @@
|
||||
import { Flex, Text } from '@chakra-ui/react';
|
||||
|
||||
import { ParameterLayoutHeader } from './ParameterLayoutHeader';
|
||||
|
||||
export type ParameterLayoutHeaderBaseProps = {
|
||||
title: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
export const ParameterLayoutHeaderBase = ({
|
||||
title,
|
||||
description,
|
||||
}: ParameterLayoutHeaderBaseProps) => {
|
||||
return (
|
||||
<ParameterLayoutHeader>
|
||||
<Flex direction="column">
|
||||
<Text fontSize="25px" fontWeight="bold">
|
||||
{title}
|
||||
</Text>
|
||||
{description && <Text>{description}</Text>}
|
||||
</Flex>
|
||||
</ParameterLayoutHeader>
|
||||
);
|
||||
};
|
24
front/src/components/ParameterLayout/ParameterLayoutRoot.tsx
Normal file
24
front/src/components/ParameterLayout/ParameterLayoutRoot.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { VStack } from '@chakra-ui/react';
|
||||
|
||||
export type ParameterLayoutRootProps = {
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
export const ParameterLayoutRoot = ({ children }: ParameterLayoutRootProps) => {
|
||||
return (
|
||||
<VStack
|
||||
gap="0px"
|
||||
marginX="15%"
|
||||
marginY="20px"
|
||||
justify="center"
|
||||
//borderRadius="20px"
|
||||
borderRadius="0px"
|
||||
border="1px solid black"
|
||||
background="gray.500"
|
||||
>
|
||||
{children}
|
||||
</VStack>
|
||||
);
|
||||
};
|
1
front/src/components/ParameterLayout/index.ts
Normal file
1
front/src/components/ParameterLayout/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * as ParameterLayout from './ParameterLayout';
|
53
front/src/components/SearchInput.tsx
Normal file
53
front/src/components/SearchInput.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import { Group, Input } from '@chakra-ui/react';
|
||||
import { MdSearch } from 'react-icons/md';
|
||||
|
||||
export type SearchInputProps = {
|
||||
onChange?: (data?: string) => void;
|
||||
onSubmit?: (data?: string) => void;
|
||||
};
|
||||
|
||||
export const SearchInput = ({
|
||||
onChange: onChangeValue,
|
||||
onSubmit: onSubmitValue,
|
||||
}: SearchInputProps) => {
|
||||
const [inputData, setInputData] = useState<string | undefined>(undefined);
|
||||
const [searchInputProperty, setSearchInputProperty] =
|
||||
useState<any>(undefined);
|
||||
function onFocusKeep(): void {
|
||||
setSearchInputProperty({
|
||||
width: '70%',
|
||||
maxWidth: '70%',
|
||||
});
|
||||
}
|
||||
function onFocusLost(): void {
|
||||
setSearchInputProperty({
|
||||
width: '250px',
|
||||
});
|
||||
}
|
||||
function onChange(event): void {
|
||||
const data =
|
||||
event.target.value.length === 0 ? undefined : event.target.value;
|
||||
setInputData(data);
|
||||
if (onChangeValue) {
|
||||
onChangeValue(data);
|
||||
}
|
||||
}
|
||||
function onSubmit(): void {
|
||||
if (onSubmitValue) {
|
||||
onSubmitValue(inputData);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<Group maxWidth="200px" marginLeft="auto" {...searchInputProperty}>
|
||||
<MdSearch color="gray.300" />
|
||||
<Input
|
||||
onFocus={onFocusKeep}
|
||||
onBlur={() => setTimeout(() => onFocusLost(), 200)}
|
||||
onChange={onChange}
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
};
|
277
front/src/components/TopBar/TopBar.tsx
Normal file
277
front/src/components/TopBar/TopBar.tsx
Normal file
@ -0,0 +1,277 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
|
||||
|
||||
import { Box, Button, ConditionalValue, Flex, HStack, IconButton, Span, Text, useDisclosure } from '@chakra-ui/react';
|
||||
import { LuAlignJustify, LuArrowBigLeft, LuCircleUserRound, LuKeySquare, LuLogIn, LuLogOut, LuMoon, LuSettings, LuSun } from 'react-icons/lu';
|
||||
import { MdHelp, MdHome, MdMore, MdOutlinePlaylistPlay, MdOutlineUploadFile, MdSupervisedUserCircle } from 'react-icons/md';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
|
||||
|
||||
import { useColorMode, useColorModeValue } from '@/components/ui/color-mode';
|
||||
import { DrawerBody, DrawerContent, DrawerHeader, DrawerRoot } from '@/components/ui/drawer';
|
||||
import { MenuContent, MenuItem, MenuRoot, MenuTrigger } from '@/components/ui/menu';
|
||||
import { environment } from '@/environment';
|
||||
import { useServiceContext } from '@/service/ServiceContext';
|
||||
import { useSessionService } from '@/service/session';
|
||||
import { colors } from '@/theme/colors';
|
||||
import { requestOpenSite, requestSignIn, requestSignOut, requestSignUp } from '@/utils/sso';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const TOP_BAR_HEIGHT = '50px';
|
||||
|
||||
export const BUTTON_TOP_BAR_PROPERTY = {
|
||||
variant: 'ghost' as ConditionalValue<
|
||||
'ghost' | 'outline' | 'solid' | 'subtle' | 'surface' | 'plain' | undefined
|
||||
>,
|
||||
//colorPalette: "brand",
|
||||
fontSize: '20px',
|
||||
textTransform: 'uppercase',
|
||||
height: TOP_BAR_HEIGHT,
|
||||
};
|
||||
|
||||
export type TopBarProps = {
|
||||
children?: ReactNode;
|
||||
title?: string;
|
||||
};
|
||||
|
||||
const ButtonMenuLeft = ({
|
||||
dest,
|
||||
title,
|
||||
icon,
|
||||
onClickEnd = () => {},
|
||||
}: {
|
||||
dest: string;
|
||||
title: string;
|
||||
icon: ReactNode;
|
||||
onClickEnd?: () => void;
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
background="#00000000"
|
||||
borderRadius="0px"
|
||||
onClick={() => {
|
||||
navigate(dest);
|
||||
onClickEnd();
|
||||
}}
|
||||
width="full"
|
||||
{...BUTTON_TOP_BAR_PROPERTY}
|
||||
>
|
||||
<Box asChild style={{ width: '45px', height: '45px' }}>
|
||||
{icon}
|
||||
</Box>
|
||||
<Text paddingLeft="3px" fontWeight="bold" marginRight="auto">
|
||||
{title}
|
||||
</Text>
|
||||
</Button>
|
||||
<Box marginY="5" marginX="10" height="2px" background="brand.600" />
|
||||
</>
|
||||
);
|
||||
};
|
||||
export const TopBar = ({ title, children }: TopBarProps) => {
|
||||
const navigate = useNavigate();
|
||||
const { colorMode, toggleColorMode } = useColorMode();
|
||||
const { session } = useServiceContext();
|
||||
const { clearToken } = useSessionService();
|
||||
const backColor = useColorModeValue('back.100', 'back.800');
|
||||
const drawerDisclose = useDisclosure();
|
||||
const onChangeTheme = () => {
|
||||
drawerDisclose.onOpen();
|
||||
};
|
||||
const onSignIn = (): void => {
|
||||
clearToken();
|
||||
requestSignIn();
|
||||
};
|
||||
const onSignUp = (): void => {
|
||||
clearToken();
|
||||
requestSignUp();
|
||||
};
|
||||
const onSignOut = (): void => {
|
||||
clearToken();
|
||||
requestSignOut();
|
||||
};
|
||||
const onKarso = (): void => {
|
||||
requestOpenSite();
|
||||
};
|
||||
return (
|
||||
<Flex
|
||||
position="absolute"
|
||||
top={0}
|
||||
left={0}
|
||||
right={0}
|
||||
height={TOP_BAR_HEIGHT}
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
backgroundColor={backColor}
|
||||
gap="2"
|
||||
px="2"
|
||||
boxShadow={'0px 2px 4px ' + colors.back[900]}
|
||||
zIndex={200}
|
||||
>
|
||||
{session?.isConnected ?
|
||||
<Button {...BUTTON_TOP_BAR_PROPERTY} onClick={onChangeTheme}>
|
||||
<HStack>
|
||||
<LuAlignJustify />
|
||||
<Text paddingLeft="3px" fontWeight="bold">
|
||||
{environment.applName}
|
||||
</Text>
|
||||
</HStack>
|
||||
</Button>
|
||||
|
||||
:(
|
||||
<HStack {...BUTTON_TOP_BAR_PROPERTY} >
|
||||
<LuAlignJustify />
|
||||
<Span paddingLeft="3px" fontWeight="bold">{environment.applName}</Span>
|
||||
</HStack>)
|
||||
}
|
||||
{title && (
|
||||
<Text
|
||||
fontSize="20px"
|
||||
fontWeight="bold"
|
||||
textTransform="uppercase"
|
||||
marginRight="auto"
|
||||
userSelect="none"
|
||||
color="brand.500"
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
)}
|
||||
{children}
|
||||
<Flex right="0">
|
||||
{!session?.isConnected && (
|
||||
<>
|
||||
<Button {...BUTTON_TOP_BAR_PROPERTY} onClick={onSignIn}>
|
||||
<LuLogIn />
|
||||
<Text paddingLeft="3px" fontWeight="bold">
|
||||
Sign-in
|
||||
</Text>
|
||||
</Button>
|
||||
<Button
|
||||
{...BUTTON_TOP_BAR_PROPERTY}
|
||||
onClick={onSignUp}
|
||||
disabled={true}
|
||||
>
|
||||
<MdMore />
|
||||
<Text paddingLeft="3px" fontWeight="bold">
|
||||
Sign-up
|
||||
</Text>
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{session?.isConnected && (
|
||||
<MenuRoot>
|
||||
<MenuTrigger asChild>
|
||||
<IconButton {...BUTTON_TOP_BAR_PROPERTY} width={TOP_BAR_HEIGHT}>
|
||||
<LuCircleUserRound />
|
||||
</IconButton>
|
||||
</MenuTrigger>
|
||||
<MenuContent>
|
||||
<MenuItem
|
||||
value="user"
|
||||
valueText="user"
|
||||
color={useColorModeValue('brand.800', 'brand.200')}
|
||||
>
|
||||
<MdSupervisedUserCircle />
|
||||
<Box flex="1">Sign in as {session?.login ?? 'Fail'}</Box>
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
value="Settings"
|
||||
valueText="Settings"
|
||||
onClick={() => navigate('/settings')}
|
||||
>
|
||||
<LuSettings />
|
||||
Settings
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
value="Help"
|
||||
valueText="Help"
|
||||
onClick={() => navigate('/help')}
|
||||
>
|
||||
<MdHelp /> Help
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
value="Sign-out"
|
||||
valueText="Sign-out"
|
||||
onClick={onSignOut}
|
||||
>
|
||||
<LuLogOut /> Sign-out
|
||||
</MenuItem>
|
||||
<MenuItem value="karso" valueText="Karso" onClick={onKarso}>
|
||||
<LuKeySquare /> Karso (SSO)
|
||||
</MenuItem>
|
||||
{colorMode === 'light' ? (
|
||||
<MenuItem
|
||||
value="set-dark"
|
||||
valueText="set-dark"
|
||||
onClick={toggleColorMode}
|
||||
>
|
||||
<LuMoon /> Set dark mode
|
||||
</MenuItem>
|
||||
) : (
|
||||
<MenuItem
|
||||
value="set-light"
|
||||
valueText="set-light"
|
||||
onClick={toggleColorMode}
|
||||
>
|
||||
<LuSun /> Set light mode
|
||||
</MenuItem>
|
||||
)}
|
||||
</MenuContent>
|
||||
</MenuRoot>
|
||||
)}
|
||||
</Flex>
|
||||
{session?.isConnected &&
|
||||
<DrawerRoot
|
||||
placement="start"
|
||||
onOpenChange={drawerDisclose.onClose}
|
||||
open={drawerDisclose.open}
|
||||
data-testid="top-bar_drawer-root"
|
||||
>
|
||||
<DrawerContent data-testid="top-bar_drawer-content">
|
||||
<DrawerHeader
|
||||
paddingY="auto"
|
||||
as="button"
|
||||
onClick={drawerDisclose.onClose}
|
||||
boxShadow={'0px 2px 4px ' + colors.back[900]}
|
||||
backgroundColor={backColor}
|
||||
color={useColorModeValue('brand.900', 'brand.50')}
|
||||
textTransform="uppercase"
|
||||
>
|
||||
<HStack {...BUTTON_TOP_BAR_PROPERTY} cursor="pointer">
|
||||
<LuArrowBigLeft />
|
||||
<Span paddingLeft="3px">{environment.applName}</Span>
|
||||
</HStack>
|
||||
</DrawerHeader>
|
||||
<DrawerBody paddingX="0px">
|
||||
<Box marginY="3" />
|
||||
<ButtonMenuLeft
|
||||
onClickEnd={drawerDisclose.onClose}
|
||||
dest="/"
|
||||
title="Home"
|
||||
icon={<MdHome />}
|
||||
/>
|
||||
<ButtonMenuLeft
|
||||
onClickEnd={drawerDisclose.onClose}
|
||||
dest="/on-air"
|
||||
title="On air"
|
||||
icon={<MdOutlinePlaylistPlay />}
|
||||
/>
|
||||
<ButtonMenuLeft
|
||||
onClickEnd={drawerDisclose.onClose}
|
||||
dest="/add"
|
||||
title="Add Media"
|
||||
icon={<MdOutlineUploadFile />}
|
||||
/>
|
||||
</DrawerBody>
|
||||
</DrawerContent>
|
||||
</DrawerRoot>
|
||||
}
|
||||
</Flex>
|
||||
);
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user