Compare commits

...

35 Commits
main ... mongo

Author SHA1 Message Date
2c0fb99202 [FEAT] update with archidata mongo interface 2024-10-12 10:46:02 +02:00
1cf08390ec [FEAT] think about mongo 2024-09-25 20:26:24 +02:00
6107b95dcd [FEAT] better management of upload files 2024-09-20 22:19:10 +02:00
e9af64405b [DEV] add checker 2024-09-19 21:22:35 +02:00
725cd54d92 [FEAT] basic pop-up and upload a single file (can not read) 2024-09-01 23:10:40 +02:00
08eb0f878a [FEAT] ready gor update and suggestion 2024-09-01 23:10:40 +02:00
619e9aa4b8 [FEAT] update the Slector to support add element 2024-09-16 21:53:29 +02:00
6181022814 [FEAT] add basic page of the ADD media (kanva OK, no upload and check) 2024-09-16 00:06:37 +02:00
3f96b870a0 [FEAT] ready to generate production 2024-09-16 00:05:53 +02:00
5eba6d32b6 [DEV] build docker and correct some typing 2024-09-15 17:58:34 +02:00
a24484b5f9 [FEAT] ignore env_dev/data* 2024-09-15 17:58:34 +02:00
77f8527fc4 [FEAT] rename front2 as front 2024-09-15 17:58:24 +02:00
75cca15898 [FEAT] remove angular front 2024-09-15 17:56:48 +02:00
f0b2a859db [FEAT] correct some connection/disconnection of SSO 2024-09-15 17:56:48 +02:00
a6fe889d4c [FEAT] better covers, Icons and work on better display for the phones 2024-09-15 17:56:48 +02:00
ad7ff2c340 [FEAT] upgrade the api generation and search how to manage cover auto-change 2024-09-15 17:56:48 +02:00
1271885124 [FIX] correct the API generation 2024-09-15 17:56:48 +02:00
dde514a1a0 [FIX] missing migration 2024-09-15 17:56:48 +02:00
dc7131843d [FEAT] continue integration 2024-09-15 17:56:48 +02:00
091390e025 [FEAT] add edit of album and artist
Missing cover that might be studied
2024-09-15 17:56:48 +02:00
3377f80fbc [FEAT] generize the form model 2024-09-15 17:56:48 +02:00
4cf71f35b6 [FEAT] review some namings 2024-09-15 17:56:48 +02:00
faaf3b280b [FEAT] live edit, remove is operational with confirm pop-in.
need to upgrade the code to be reusable
2024-09-15 17:56:48 +02:00
0687369164 [DEV] test 2 2024-09-15 17:56:48 +02:00
147264eb7f [DEV] test 1 2024-09-15 17:56:48 +02:00
ec93c31acb Work on form and basic tools I need 2024-09-15 17:56:48 +02:00
873f0acff5 [DEV] integrate menue on thack 2024-09-15 17:56:48 +02:00
244f8a988e [DEV] remove all async it is too slow... 2024-09-15 17:56:48 +02:00
d49e1d06fe [FEAT] so many things .... 2024-09-15 17:56:48 +02:00
53c3cfefe3 [FEAT] corect the useMemo and better interface of audio player 2024-09-15 17:56:48 +02:00
90fb1ed411 [FEAT] add album direct diaplay 2024-09-15 17:56:48 +02:00
30bc82edb3 [FEAT] basic working karusic 2024-09-15 17:56:40 +02:00
1d4c547d89 [DEV] first vesion of karisic in react + chakra-ui 2024-08-22 11:29:57 +02:00
21d53b77f2 [FEAT] many renaming 2024-08-15 11:47:50 +02:00
3b27edfa3b [VERSION] update dev tag version 2024-05-12 12:03:31 +02:00
349 changed files with 23143 additions and 22405 deletions

3
.gitignore vendored
View File

@ -53,6 +53,8 @@ testem.log
# System Files
.DS_Store
Thumbs.db
/env_dev/data
/env_dev/dataMongo
backPY/env
@ -62,3 +64,4 @@ __pycache__
.design/
.vscode/
front/storybook-static

View File

@ -5,7 +5,7 @@ version_file="../version.txt"
# update new release dependency
cd back
# update the Maven version number
mvn versions:set -DnewVersion=$(sed 's/DEV/SNAPSHOT/g' $version_file)
mvn versions:set -DnewVersion=$(sed 's/dev/SNAPSHOT/g' $version_file)
if grep -q "DEV" "$version_file"; then
# update all versions release of dependency
mvn versions:use-latest-releases
@ -19,11 +19,14 @@ cd -
cd front
if grep -q "DEV" "$version_file"; then
if grep -q "dev" "$version_file"; then
# update all dependency
pnpm install
pnpm run update_packages
else
# in case of release ==> can not do it automatically ...
echo not implemented
fi
cd -

View File

@ -3,23 +3,31 @@
## buyilding-end install applications:
##
######################################################################################
FROM archlinux:base-devel AS builder
FROM archlinux:base-devel AS common
# update system
RUN pacman -Syu --noconfirm && pacman-db-upgrade \
&& pacman -S --noconfirm jdk-openjdk maven npm pnpm \
&& pacman -S --noconfirm jdk-openjdk \
&& pacman -Scc --noconfirm
WORKDIR /tmp
FROM common AS builder
# update system
RUN pacman -Syu --noconfirm && pacman-db-upgrade \
&& pacman -S --noconfirm maven npm pnpm \
&& pacman -Scc --noconfirm
ENV PATH /tmp/node_modules/.bin:$PATH
WORKDIR /tmp
######################################################################################
##
## Build back:
##
######################################################################################
FROM builder AS buildBack
COPY back/pom.xml /tmp
COPY back/src /tmp/src/
FROM builder AS build_back
COPY back/pom.xml ./
COPY back/Formatter.xml ./
COPY back/src ./src/
RUN mvn clean compile assembly:single
######################################################################################
@ -27,27 +35,44 @@ RUN mvn clean compile assembly:single
## Build front:
##
######################################################################################
FROM builder AS buildFront
FROM builder AS dependency_front
RUN echo "@kangaroo-and-rabbit:registry=https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/npm/" > /root/.npmrc
ADD front/package.json \
front/karma.conf.js \
front/protractor.conf.js \
/tmp/
front/pnpm-lock.yaml \
./
ADD front/src/theme ./src/theme
# install and cache app dependencies
RUN pnpm install
RUN pnpm install --prod=false
ADD front/e2e \
###############################################################
## Install sources
###############################################################
FROM dependency_front AS load_sources_front
# JUST to get the vertion of the application and his sha...
COPY front/build.js \
front/version.txt \
front/tsconfig.json \
front/tslint.json \
front/angular.json \
/tmp/
ADD front/src /tmp/src
front/tsconfig.node.json \
front/vite.config.mts \
front/index.html \
./
COPY front/public ./public
COPY front/src ./src
# generate build
RUN ng build --output-path=dist --configuration=production --base-href=/karusic/ --deploy-url=/karusic/
#We are not in prod mode ==> we need to overwrite the production env.
ARG env=front/.env.production
COPY ${env} .env
###############################################################
## Build the sources
###############################################################
FROM load_sources_front AS build_front
# build in bundle mode all the application
RUN pnpm static:build
######################################################################################
##
@ -55,9 +80,10 @@ RUN ng build --output-path=dist --configuration=production --base-href=/karusic/
##
######################################################################################
FROM bellsoft/liberica-openjdk-alpine:latest
# add wget to manage the health check...
RUN apk add --no-cache wget
#FROM bellsoft/liberica-openjdk-alpine:latest
## add wget to manage the health check...
#RUN apk add --no-cache wget
FROM common
#FROM archlinux:base
#RUN pacman -Syu --noconfirm && pacman-db-upgrade
@ -68,10 +94,10 @@ RUN apk add --no-cache wget
## clean all the caches Need only on the release environment
#RUN pacman -Scc --noconfirm
ENV LANG=C.UTF-8
ENV LANG C.UTF-8
COPY --from=buildBack /tmp/out/maven/*.jar /application/application.jar
COPY --from=buildFront /tmp/dist /application/front/
COPY --from=build_back /tmp/out/maven/*.jar /application/application.jar
COPY --from=build_front /tmp/dist /application/front/
WORKDIR /application/

View File

@ -1,4 +0,0 @@

View File

@ -1,10 +1,10 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.kar</groupId>
<artifactId>karusic</artifactId>
<version>0.1.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>
@ -20,17 +20,17 @@
<dependency>
<groupId>kangaroo-and-rabbit</groupId>
<artifactId>archidata</artifactId>
<version>0.7.1</version>
<version>0.14.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.9</version>
<version>2.1.0-alpha1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.16.1</version>
<version>2.18.0-rc1</version>
</dependency>
<!--
************************************************************
@ -40,15 +40,25 @@
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.1</version>
<version>5.11.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.10.1</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.1</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.5.0</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
@ -186,23 +196,10 @@
</configuration>
</plugin>
<!-- Check the style of the code -->
<!--
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<configLocation>CheckStyle.xml</configLocation>
<consoleOutput>true</consoleOutput>
<failOnViolation>true</failOnViolation>
<failsOnError>true</failsOnError>
<includeTestSourceDirectory>true</includeTestSourceDirectory>
</configuration>
</plugin>
<plugin>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>2.12.2</version>
<version>2.23.0</version>
<configuration>
<encoding>UTF-8</encoding>
<lineEnding>LF</lineEnding>
@ -226,7 +223,23 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.8.5.0</version>
<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>
</build>
<!-- Generate Java-docs As Part Of Project Reports -->

View File

@ -0,0 +1,17 @@
package org.kar.karusic;
import org.kar.archidata.db.DbInterfaceMorphia;
import org.kar.karusic.model.Album;
import org.kar.karusic.model.Artist;
import org.kar.karusic.model.Gender;
import org.kar.karusic.model.Playlist;
import org.kar.karusic.model.Track;
public class MorphiaService extends DbInterfaceMorphia {
public MorphiaService() {
super("mongodb://root:base_db_password@localhost:27017", "karusic", Album.class, Artist.class, Gender.class, Playlist.class, Track.class);
}
}

View File

@ -1,6 +1,11 @@
package org.kar.karusic;
import java.net.URI;
import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriter;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
@ -10,11 +15,9 @@ 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.ExceptionCatcher;
import org.kar.archidata.catcher.FailExceptionCatcher;
import org.kar.archidata.catcher.InputExceptionCatcher;
import org.kar.archidata.catcher.JacksonCatcher;
import org.kar.archidata.catcher.SystemExceptionCatcher;
import org.kar.archidata.api.ProxyResource;
import org.kar.archidata.catcher.GenericCatcher;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.filter.CORSFilter;
import org.kar.archidata.filter.OptionFilter;
import org.kar.archidata.migration.MigrationEngine;
@ -32,6 +35,7 @@ import org.kar.karusic.migration.Initialization;
import org.kar.karusic.migration.Migration20231126;
import org.kar.karusic.migration.Migration20240225;
import org.kar.karusic.migration.Migration20240226;
import org.kar.karusic.migration.Migration20240907;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -42,8 +46,11 @@ public class WebLauncher {
protected UpdateJwtPublicKey keyUpdater = null;
protected HttpServer server = null;
private final DataAccess da;
public WebLauncher() {
ConfigBaseVariable.bdDatabase = "karusic";
this.da = DataAccess.createInterface();
}
private static URI getBaseURI() {
@ -52,15 +59,16 @@ public class WebLauncher {
public void migrateDB() throws Exception {
WebLauncher.LOGGER.info("Create migration engine");
final MigrationEngine migrationEngine = new MigrationEngine();
final MigrationEngine migrationEngine = new MigrationEngine(this.da);
WebLauncher.LOGGER.info("Add initialization");
migrationEngine.setInit(new Initialization());
WebLauncher.LOGGER.info("Add migration since last version");
migrationEngine.add(new Migration20231126());
migrationEngine.add(new Migration20240225());
migrationEngine.add(new Migration20240226());
migrationEngine.add(new Migration20240907());
WebLauncher.LOGGER.info("Migrate the DB [START]");
migrationEngine.migrateWaitAdmin(GlobalConfiguration.dbConfig);
migrationEngine.migrateWaitAdmin(GlobalConfiguration.getDbconfig());
WebLauncher.LOGGER.info("Migrate the DB [STOP]");
}
@ -77,7 +85,34 @@ public class WebLauncher {
WebLauncher.LOGGER.info("STOP the REST server:");
}
public void plop(final String aaa) {
// List available Image Readers
System.out.println("Available Image Readers:");
final Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName(aaa);
while (readers.hasNext()) {
final ImageReader reader = readers.next();
System.out.println("Reader: " + reader.getOriginatingProvider().getDescription(null));
System.out.println("Reader CN: " + reader.getOriginatingProvider().getPluginClassName());
// ImageIO.deregisterServiceProvider(reader.getOriginatingProvider());
}
// List available Image Writers
System.out.println("\nAvailable Image Writers:");
final Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(aaa);
while (writers.hasNext()) {
final ImageWriter writer = writers.next();
System.out.println("Writer: " + writer.getOriginatingProvider().getDescription(null));
System.out.println("Writer CN: " + writer.getOriginatingProvider().getPluginClassName());
}
}
public void process() throws InterruptedException {
ImageIO.scanForPlugins();
plop("jpeg");
plop("png");
plop("webmp");
plop("webp");
// ===================================================================
// Configure resources
// ===================================================================
@ -92,11 +127,7 @@ public class WebLauncher {
// global authentication system
rc.register(KarusicAuthenticationFilter.class);
// register exception catcher
rc.register(JacksonCatcher.class);
rc.register(InputExceptionCatcher.class);
rc.register(SystemExceptionCatcher.class);
rc.register(FailExceptionCatcher.class);
rc.register(ExceptionCatcher.class);
GenericCatcher.addAll(rc);
// add default resource:
rc.register(UserResource.class);
rc.register(AlbumResource.class);
@ -105,6 +136,7 @@ public class WebLauncher {
rc.register(PlaylistResource.class);
rc.register(TrackResource.class);
rc.register(DataResource.class);
rc.register(ProxyResource.class);
rc.register(HealthCheck.class);
rc.register(Front.class);
@ -112,15 +144,15 @@ public class WebLauncher {
// add jackson to be discover when we are ins standalone server
rc.register(JacksonFeature.class);
// enable this to show low level request
//rc.property(LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_SERVER, Level.WARNING.getName());
// rc.property(LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_SERVER, Level.WARNING.getName());
//System.out.println("Connect on the BDD:");
//System.out.println(" getDBHost: '" + ConfigVariable.getDBHost() + "'");
//System.out.println(" getDBPort: '" + ConfigVariable.getDBPort() + "'");
//System.out.println(" getDBLogin: '" + ConfigVariable.getDBLogin() + "'");
//System.out.println(" getDBPassword: '" + ConfigVariable.getDBPassword() + "'");
//System.out.println(" getDBName: '" + ConfigVariable.getDBName() + "'");
System.out.println(" ==> " + GlobalConfiguration.dbConfig);
// System.out.println("Connect on the BDD:");
// System.out.println(" getDBHost: '" + ConfigVariable.getDBHost() + "'");
// System.out.println(" getDBPort: '" + ConfigVariable.getDBPort() + "'");
// System.out.println(" getDBLogin: '" + ConfigVariable.getDBLogin() + "'");
// System.out.println(" getDBPassword: '" + ConfigVariable.getDBPassword() + "'");
// System.out.println(" getDBName: '" + ConfigVariable.getDBName() + "'");
System.out.println(" ==> " + GlobalConfiguration.getDbconfig());
System.out.println("OAuth service " + getBaseURI());
this.server = GrizzlyHttpServerFactory.createHttpServer(getBaseURI(), rc);
final HttpServer serverLink = this.server;

View File

@ -3,7 +3,9 @@ package org.kar.karusic;
import java.util.List;
import org.kar.archidata.api.DataResource;
import org.kar.archidata.dataAccess.DataFactoryTsApi;
import org.kar.archidata.api.ProxyResource;
import org.kar.archidata.externalRestApi.AnalyzeApi;
import org.kar.archidata.externalRestApi.TsGenerateApi;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.kar.karusic.api.AlbumResource;
import org.kar.karusic.api.ArtistResource;
@ -13,7 +15,6 @@ import org.kar.karusic.api.HealthCheck;
import org.kar.karusic.api.PlaylistResource;
import org.kar.karusic.api.TrackResource;
import org.kar.karusic.api.UserResource;
import org.kar.karusic.migration.Initialization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -22,18 +23,18 @@ public class WebLauncherLocal extends WebLauncher {
private WebLauncherLocal() {}
public static void generateObjects() throws Exception {
LOGGER.info("Generate APIs");
final List<Class<?>> listOfResources = List.of(AlbumResource.class, ArtistResource.class, Front.class, GenderResource.class, HealthCheck.class, PlaylistResource.class, UserResource.class,
TrackResource.class, DataResource.class, ProxyResource.class);
final AnalyzeApi api = new AnalyzeApi();
api.addAllApi(listOfResources);
TsGenerateApi.generateApi(api, "../front/src/back-api/");
LOGGER.info("Generate APIs (DONE)");
}
public static void main(final String[] args) throws Exception {
DataFactoryTsApi.generatePackage(List.of(
AlbumResource.class,
ArtistResource.class,
Front.class,
GenderResource.class,
HealthCheck.class,
PlaylistResource.class,
UserResource.class,
TrackResource.class,
DataResource.class),
Initialization.CLASSES_BASE, "../front/src/app/back-api/");
generateObjects();
final WebLauncherLocal launcher = new WebLauncherLocal();
launcher.process();
launcher.LOGGER.info("end-configure the server & wait finish process:");
@ -46,8 +47,9 @@ public class WebLauncherLocal extends WebLauncher {
if (true) {
// for local test:
ConfigBaseVariable.apiAdress = "http://0.0.0.0:19080/karusic/api/";
//ConfigBaseVariable.ssoAdress = "https://atria-soft.org/karso/api/";
// ConfigBaseVariable.ssoAdress = "https://atria-soft.org/karso/api/";
ConfigBaseVariable.dbPort = "3906";
ConfigBaseVariable.testMode = "true";
}
try {
super.migrateDB();

View File

@ -7,12 +7,14 @@ import java.util.UUID;
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.FormDataOptional;
import org.kar.archidata.annotation.TypeScriptProgress;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.addOn.AddOnDataJson;
import org.kar.archidata.dataAccess.addOn.AddOnManyToMany;
import org.kar.archidata.dataAccess.options.CheckFunction;
import org.kar.archidata.tools.DataTools;
import org.kar.karusic.model.Album;
import org.kar.karusic.model.Album.AlbumChecker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -27,34 +29,45 @@ import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
@Path("/album")
@Produces({ MediaType.APPLICATION_JSON })
public class AlbumResource {
private static final Logger LOGGER = LoggerFactory.getLogger(AlbumResource.class);
static final AlbumChecker CHECKER = new AlbumChecker();
private final DataAccess da = DataAccess.createInterface();
@GET
@Path("{id}")
@RolesAllowed("USER")
@Operation(description = "Get a specific Album with his ID")
public static Album get(@PathParam("id") final Long id) throws Exception {
return DataAccess.get(Album.class, id);
public Album get(@PathParam("id") final Long id) throws Exception {
return this.da.get(Album.class, id);
// return this.morphiaService.getDatastore().find(Album.class).filter(Filters.eq("id", id)).first();
}
@GET
@RolesAllowed("USER")
@Operation(description = "Get all the available Albums")
public List<Album> gets() throws Exception {
return DataAccess.gets(Album.class);
return this.da.gets(Album.class);
// final Query<Album> query = this.morphiaService.getDatastore().find(Album.class);
// return query.stream().toList();
}
@POST
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
@Operation(description = "Add an album (when all the data already exist)")
public Album post(@AsyncType(Album.class) final String jsonRequest) throws Exception {
return DataAccess.insertWithJson(Album.class, jsonRequest);
public Album post(final Album data) throws Exception {
// TODO: how to manage the checker ???
// final Album ret = this.morphiaService.getDatastore().save(data);
// return ret;
/* final MongoCollection<Track> trackCollection = db.getCollection("TTRACLK", Track.class); final InsertOneResult res = trackCollection.insertOne(plop); LOGGER.warn("plpop {}", res); final
* ObjectId ploppppp = res.getInsertedId().asObjectId().getValue(); LOGGER.warn("plpop 2522 {}", res.getInsertedId().asObjectId().getValue()); final Track ret =
* trackCollection.find(Filters.eq("_id", res.getInsertedId().asObjectId().getValue())) .first(); System.out.println("Grade found:\t" + ret); */
return this.da.insert(data, new CheckFunction(CHECKER));
}
@PATCH
@ -63,47 +76,59 @@ public class AlbumResource {
@Consumes(MediaType.APPLICATION_JSON)
@Operation(description = "Update a specific album")
public Album patch(@PathParam("id") final Long id, @AsyncType(Album.class) final String jsonRequest) throws Exception {
DataAccess.updateWithJson(Album.class, id, jsonRequest);
return DataAccess.get(Album.class, id);
// final Query<Album> query = this.morphiaService.getDatastore().find(Album.class).filter(Filters.eq("id", id));
// final UpdateOperations<Album> ops = this.morphiaService.getDatastore().createUpdateOperations(Album.class)
// .set("name", master.getName());
// this.morphiaService.getDatastore().update(query, ops);
// return Response.ok(master).build();
this.da.updateWithJson(Album.class, id, jsonRequest, new CheckFunction(CHECKER));
return this.da.get(Album.class, id);
}
// @PUT
// @Path("{id}")
// @RolesAllowed("ADMIN")
// @Consumes(MediaType.APPLICATION_JSON)
// @Operation(description = "Update a specific album")
// public Album put(@PathParam("id") final Long id, final Album album)
// throws Exception {
// final Query<Album> query = this.morphiaService.getDatastore().find(Album.class).filter(Filters.eq("id", id));
// final UpdateOperations<Album> ops = this.morphiaService.getDatastore().createUpdateOperations(Album.class)
// .set("name", album.getName());
// this.morphiaService.getDatastore().update(query, ops);
// return Response.ok(album).build();
// }
@DELETE
@Path("{id}")
@RolesAllowed("ADMIN")
@Operation(description = "Remove a specific album")
public void remove(@PathParam("id") final Long id) throws Exception {
DataAccess.delete(Album.class, id);
}
@POST
@Path("{id}/track/{trackId}")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@Operation(description = "Add a Track on a specific album")
public Album addTrack(@PathParam("id") final Long id, @PathParam("trackId") final Long trackId) throws Exception {
AddOnManyToMany.removeLink(Album.class, id, "track", trackId);
return DataAccess.get(Album.class, id);
}
@DELETE
@Path("{id}/track/{trackId}")
@RolesAllowed("ADMIN")
@Operation(description = "Remove a Track on a specific album")
public Album removeTrack(@PathParam("id") final Long id, @PathParam("trackId") final Long trackId) throws Exception {
AddOnManyToMany.removeLink(Album.class, id, "track", trackId);
return DataAccess.get(Album.class, id);
this.da.delete(Album.class, id);
// this.morphiaService.getDatastore().find(Album.class).filter(Filters.eq("id", id)).delete();
}
/* @POST
* @Path("{id}/track/{trackId}")
* @RolesAllowed("ADMIN")
* @Consumes({ MediaType.MULTIPART_FORM_DATA })
* @Operation(description = "Add a Track on a specific album") public Album addTrack(@PathParam("id") final Long id, @PathParam("trackId") final Long trackId) throws Exception {
* AddOnManyToMany.removeLink(this.dam, Album.class, id, "track", trackId); return this.dam.get(Album.class, id); } */
@POST
@Path("{id}/cover")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@Operation(description = "Add a cover on a specific album")
@AsyncType(Album.class)
@TypeScriptProgress
public Response uploadCover(@PathParam("id") final Long id, @FormDataParam("fileName") final String fileName, @FormDataParam("file") final InputStream fileInputStream,
@FormDataParam("file") final FormDataContentDisposition fileMetaData) {
return DataTools.uploadCover(Album.class, id, fileName, fileInputStream, fileMetaData);
public Album uploadCover(@PathParam("id") final Long id, @FormDataOptional @FormDataParam("uri") final String uri, @FormDataOptional @FormDataParam("file") final InputStream fileInputStream,
@FormDataOptional @FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
if (uri != null) {
DataTools.uploadCoverFromUri(this.da, Album.class, id, uri);
} else {
DataTools.uploadCover(this.da, Album.class, id, fileInputStream, fileMetaData);
}
return this.da.get(Album.class, id);
}
@DELETE
@ -111,7 +136,7 @@ public class AlbumResource {
@RolesAllowed("ADMIN")
@Operation(description = "Remove a cover on a specific album")
public Album removeCover(@PathParam("id") final Long id, @PathParam("coverId") final UUID coverId) throws Exception {
AddOnDataJson.removeLink(Album.class, id, "covers", coverId);
return DataAccess.get(Album.class, id);
AddOnDataJson.removeLink(this.da, Album.class, id, "covers", coverId);
return this.da.get(Album.class, id);
}
}

View File

@ -7,11 +7,14 @@ import java.util.UUID;
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.FormDataOptional;
import org.kar.archidata.annotation.TypeScriptProgress;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.addOn.AddOnDataJson;
import org.kar.archidata.dataAccess.options.CheckFunction;
import org.kar.archidata.tools.DataTools;
import org.kar.karusic.model.Artist;
import org.kar.karusic.model.Artist.ArtistChecker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -25,31 +28,32 @@ import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
@Path("/artist")
@Produces({ MediaType.APPLICATION_JSON })
public class ArtistResource {
private static final Logger LOGGER = LoggerFactory.getLogger(ArtistResource.class);
static final ArtistChecker CHECKER = new ArtistChecker();
private final DataAccess da = DataAccess.createInterface();
@GET
@Path("{id}")
@RolesAllowed("USER")
public static Artist get(@PathParam("id") final Long id) throws Exception {
return DataAccess.get(Artist.class, id);
public Artist get(@PathParam("id") final Long id) throws Exception {
return this.da.get(Artist.class, id);
}
@GET
@RolesAllowed("USER")
public List<Artist> gets() throws Exception {
return DataAccess.gets(Artist.class);
return this.da.gets(Artist.class);
}
@POST
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Artist post(@AsyncType(Artist.class) final String jsonRequest) throws Exception {
return DataAccess.insertWithJson(Artist.class, jsonRequest);
public Artist post(final Artist data) throws Exception {
return this.da.insert(data, new CheckFunction(CHECKER));
}
@PATCH
@ -57,26 +61,30 @@ public class ArtistResource {
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Artist patch(@PathParam("id") final Long id, @AsyncType(Artist.class) final String jsonRequest) throws Exception {
DataAccess.updateWithJson(Artist.class, id, jsonRequest);
return DataAccess.get(Artist.class, id);
this.da.updateWithJson(Artist.class, id, jsonRequest, new CheckFunction(CHECKER));
return this.da.get(Artist.class, id);
}
@DELETE
@Path("{id}")
@RolesAllowed("ADMIN")
public void remove(@PathParam("id") final Long id) throws Exception {
DataAccess.delete(Artist.class, id);
this.da.delete(Artist.class, id);
}
@POST
@Path("{id}/cover")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@AsyncType(Artist.class)
@TypeScriptProgress
public Response uploadCover(@PathParam("id") final Long id, @FormDataParam("fileName") final String fileName, @FormDataParam("file") final InputStream fileInputStream,
@FormDataParam("file") final FormDataContentDisposition fileMetaData) {
return DataTools.uploadCover(Artist.class, id, fileName, fileInputStream, fileMetaData);
public Artist uploadCover(@PathParam("id") final Long id, @FormDataOptional @FormDataParam("uri") final String uri, @FormDataOptional @FormDataParam("file") final InputStream fileInputStream,
@FormDataOptional @FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
if (uri != null) {
DataTools.uploadCoverFromUri(this.da, Artist.class, id, uri);
} else {
DataTools.uploadCover(this.da, Artist.class, id, fileInputStream, fileMetaData);
}
return this.da.get(Artist.class, id);
}
@DELETE
@ -84,7 +92,7 @@ public class ArtistResource {
@RolesAllowed("ADMIN")
public Artist removeCover(@PathParam("id") final Long id, @PathParam("coverId") final UUID coverId) throws Exception {
LOGGER.error("klmlmkmlkmlklmklmk");
AddOnDataJson.removeLink(Artist.class, id, "covers", coverId);
return DataAccess.get(Artist.class, id);
AddOnDataJson.removeLink(this.da, Artist.class, id, "covers", coverId);
return this.da.get(Artist.class, id);
}
}

View File

@ -7,11 +7,14 @@ import java.util.UUID;
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.FormDataOptional;
import org.kar.archidata.annotation.TypeScriptProgress;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.addOn.AddOnDataJson;
import org.kar.archidata.dataAccess.options.CheckFunction;
import org.kar.archidata.tools.DataTools;
import org.kar.karusic.model.Gender;
import org.kar.karusic.model.Gender.GenderChecker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -25,31 +28,32 @@ import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
@Path("/gender")
@Produces({ MediaType.APPLICATION_JSON })
public class GenderResource {
private static final Logger LOGGER = LoggerFactory.getLogger(GenderResource.class);
static final GenderChecker CHECKER = new GenderChecker();
private final DataAccess da = DataAccess.createInterface();
@GET
@Path("{id}")
@RolesAllowed("USER")
public static Gender get(@PathParam("id") final Long id) throws Exception {
return DataAccess.get(Gender.class, id);
public Gender get(@PathParam("id") final Long id) throws Exception {
return this.da.get(Gender.class, id);
}
@GET
@RolesAllowed("USER")
public List<Gender> gets() throws Exception {
return DataAccess.gets(Gender.class);
return this.da.gets(Gender.class);
}
@POST
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Gender post(@AsyncType(Gender.class) final String jsonRequest) throws Exception {
return DataAccess.insertWithJson(Gender.class, jsonRequest);
public Gender post(final Gender data) throws Exception {
return this.da.insert(data, new CheckFunction(CHECKER));
}
@PATCH
@ -57,33 +61,37 @@ public class GenderResource {
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Gender patch(@PathParam("id") final Long id, @AsyncType(Gender.class) final String jsonRequest) throws Exception {
DataAccess.updateWithJson(Gender.class, id, jsonRequest);
return DataAccess.get(Gender.class, id);
this.da.updateWithJson(Gender.class, id, jsonRequest, new CheckFunction(CHECKER));
return this.da.get(Gender.class, id);
}
@DELETE
@Path("{id}")
@RolesAllowed("ADMIN")
public void remove(@PathParam("id") final Long id) throws Exception {
DataAccess.delete(Gender.class, id);
this.da.delete(Gender.class, id);
}
@POST
@Path("{id}/cover")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@AsyncType(Gender.class)
@TypeScriptProgress
public Response uploadCover(@PathParam("id") final Long id, @FormDataParam("fileName") final String fileName, @FormDataParam("file") final InputStream fileInputStream,
@FormDataParam("file") final FormDataContentDisposition fileMetaData) {
return DataTools.uploadCover(Gender.class, id, fileName, fileInputStream, fileMetaData);
public Gender uploadCover(@PathParam("id") final Long id, @FormDataOptional @FormDataParam("uri") final String uri, @FormDataOptional @FormDataParam("file") final InputStream fileInputStream,
@FormDataOptional @FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
if (uri != null) {
DataTools.uploadCoverFromUri(this.da, Gender.class, id, uri);
} else {
DataTools.uploadCover(this.da, Gender.class, id, fileInputStream, fileMetaData);
}
return this.da.get(Gender.class, id);
}
@DELETE
@Path("{id}/cover/{coverId}")
@RolesAllowed("ADMIN")
public Gender removeCover(@PathParam("id") final Long id, @PathParam("coverId") final UUID coverId) throws Exception {
AddOnDataJson.removeLink(Gender.class, id, "covers", coverId);
return DataAccess.get(Gender.class, id);
AddOnDataJson.removeLink(this.da, Gender.class, id, "covers", coverId);
return this.da.get(Gender.class, id);
}
}

View File

@ -15,8 +15,8 @@ import jakarta.ws.rs.core.Response;
@Produces(MediaType.APPLICATION_JSON)
public class HealthCheck {
public record HealthResult(
String value) {};
public record HealthResult(String value) {
};
@GET
@PermitAll

View File

@ -9,9 +9,10 @@ import org.glassfish.jersey.media.multipart.FormDataParam;
import org.kar.archidata.annotation.AsyncType;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.addOn.AddOnDataJson;
import org.kar.archidata.dataAccess.addOn.AddOnManyToMany;
import org.kar.archidata.dataAccess.options.CheckFunction;
import org.kar.archidata.tools.DataTools;
import org.kar.karusic.model.Playlist;
import org.kar.karusic.model.Track.TrackChecker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -25,31 +26,32 @@ import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
@Path("/playlist")
@Produces({ MediaType.APPLICATION_JSON })
public class PlaylistResource {
private static final Logger LOGGER = LoggerFactory.getLogger(PlaylistResource.class);
static final TrackChecker CHECKER = new TrackChecker();
private final DataAccess da = DataAccess.createInterface();
@GET
@Path("{id}")
@RolesAllowed("USER")
public static Playlist get(@PathParam("id") final Long id) throws Exception {
return DataAccess.get(Playlist.class, id);
public Playlist get(@PathParam("id") final Long id) throws Exception {
return this.da.get(Playlist.class, id);
}
@GET
@RolesAllowed("USER")
public List<Playlist> gets() throws Exception {
return DataAccess.gets(Playlist.class);
return this.da.gets(Playlist.class);
}
@POST
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Playlist post(@AsyncType(Playlist.class) final String jsonRequest) throws Exception {
return DataAccess.insertWithJson(Playlist.class, jsonRequest);
public Playlist post(final Playlist data) throws Exception {
return this.da.insert(data, new CheckFunction(CHECKER));
}
@PATCH
@ -57,15 +59,15 @@ public class PlaylistResource {
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Playlist patch(@PathParam("id") final Long id, @AsyncType(Playlist.class) final String jsonRequest) throws Exception {
DataAccess.updateWithJson(Playlist.class, id, jsonRequest);
return DataAccess.get(Playlist.class, id);
this.da.updateWithJson(Playlist.class, id, jsonRequest, new CheckFunction(CHECKER));
return this.da.get(Playlist.class, id);
}
@DELETE
@Path("{id}")
@RolesAllowed("ADMIN")
public void remove(@PathParam("id") final Long id) throws Exception {
DataAccess.delete(Playlist.class, id);
this.da.delete(Playlist.class, id);
}
@POST
@ -73,16 +75,16 @@ public class PlaylistResource {
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
public Playlist addTrack(@PathParam("id") final Long id, @PathParam("trackId") final Long trackId) throws Exception {
AddOnManyToMany.removeLink(Playlist.class, id, "track", trackId);
return DataAccess.get(Playlist.class, id);
AddOnDataJson.removeLink(this.da, Playlist.class, id, "track", trackId);
return this.da.get(Playlist.class, id);
}
@DELETE
@Path("{id}/track/{trackId}")
@RolesAllowed("ADMIN")
public Playlist removeTrack(@PathParam("id") final Long id, @PathParam("trackId") final Long trackId) throws Exception {
AddOnManyToMany.removeLink(Playlist.class, id, "track", trackId);
return DataAccess.get(Playlist.class, id);
AddOnDataJson.removeLink(this.da, Playlist.class, id, "track", trackId);
return this.da.get(Playlist.class, id);
}
@POST
@ -90,16 +92,16 @@ public class PlaylistResource {
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@AsyncType(Playlist.class)
public Response uploadCover(@PathParam("id") final Long id, @FormDataParam("fileName") final String fileName, @FormDataParam("file") final InputStream fileInputStream,
@FormDataParam("file") final FormDataContentDisposition fileMetaData) {
return DataTools.uploadCover(Playlist.class, id, fileName, fileInputStream, fileMetaData);
public void uploadCover(@PathParam("id") final Long id, @FormDataParam("file") final InputStream fileInputStream, @FormDataParam("file") final FormDataContentDisposition fileMetaData)
throws Exception {
DataTools.uploadCover(this.da, Playlist.class, id, fileInputStream, fileMetaData);
}
@DELETE
@Path("{id}/cover/{coverId}")
@RolesAllowed("ADMIN")
public Playlist removeCover(@PathParam("id") final Long id, @PathParam("coverId") final UUID coverId) throws Exception {
AddOnDataJson.removeLink(Playlist.class, id, "covers", coverId);
return DataAccess.get(Playlist.class, id);
AddOnDataJson.removeLink(this.da, Playlist.class, id, "covers", coverId);
return this.da.get(Playlist.class, id);
}
}

View File

@ -10,18 +10,15 @@ import java.util.UUID;
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.FormDataOptional;
import org.kar.archidata.annotation.TypeScriptProgress;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.addOn.AddOnDataJson;
import org.kar.archidata.dataAccess.addOn.AddOnManyToMany;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.dataAccess.options.CheckFunction;
import org.kar.archidata.model.Data;
import org.kar.archidata.tools.DataTools;
import org.kar.karusic.model.Album;
import org.kar.karusic.model.Artist;
import org.kar.karusic.model.Gender;
import org.kar.karusic.model.Track;
import org.kar.karusic.model.Track.TrackChecker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -41,25 +38,27 @@ import jakarta.ws.rs.core.Response;
@Produces({ MediaType.APPLICATION_JSON })
public class TrackResource {
private static final Logger LOGGER = LoggerFactory.getLogger(TrackResource.class);
static final TrackChecker CHECKER = new TrackChecker();
private final DataAccess da = DataAccess.createInterface();
@GET
@Path("{id}")
@RolesAllowed("USER")
public static Track get(@PathParam("id") final Long id) throws Exception {
return DataAccess.get(Track.class, id);
public Track get(@PathParam("id") final Long id) throws Exception {
return this.da.get(Track.class, id);
}
@GET
@RolesAllowed("USER")
public List<Track> gets() throws Exception {
return DataAccess.gets(Track.class);
return this.da.gets(Track.class);
}
@POST
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Track post(@AsyncType(Track.class) final String jsonRequest) throws Exception {
return DataAccess.insertWithJson(Track.class, jsonRequest);
public Track post(final Track data) throws Exception {
return this.da.insert(data, new CheckFunction(CHECKER));
}
@PATCH
@ -67,15 +66,15 @@ public class TrackResource {
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Track patch(@PathParam("id") final Long id, @AsyncType(Track.class) final String jsonRequest) throws Exception {
DataAccess.updateWithJson(Track.class, id, jsonRequest);
return DataAccess.get(Track.class, id);
this.da.updateWithJson(Track.class, id, jsonRequest, new CheckFunction(CHECKER));
return this.da.get(Track.class, id);
}
@DELETE
@Path("{id}")
@RolesAllowed("ADMIN")
public void remove(@PathParam("id") final Long id) throws Exception {
DataAccess.delete(Track.class, id);
this.da.delete(Track.class, id);
}
@POST
@ -83,92 +82,83 @@ public class TrackResource {
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
public Track addTrack(@PathParam("id") final Long id, @PathParam("artistId") final Long artistId) throws Exception {
AddOnManyToMany.removeLink(Track.class, id, "artist", artistId);
return DataAccess.get(Track.class, id);
AddOnDataJson.removeLink(this.da, Track.class, id, "artist", artistId);
return this.da.get(Track.class, id);
}
@DELETE
@Path("{id}/artist/{trackId}")
@RolesAllowed("ADMIN")
public Track removeTrack(@PathParam("id") final Long id, @PathParam("artistId") final Long artistId) throws Exception {
AddOnManyToMany.removeLink(Track.class, id, "artist", artistId);
return DataAccess.get(Track.class, id);
AddOnDataJson.removeLink(this.da, Track.class, id, "artist", artistId);
return this.da.get(Track.class, id);
}
@POST
@Path("{id}/cover")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@AsyncType(Track.class)
@TypeScriptProgress
public Response uploadCover(@PathParam("id") final Long id, @FormDataParam("fileName") final String fileName, @FormDataParam("file") final InputStream fileInputStream,
@FormDataParam("file") final FormDataContentDisposition fileMetaData) {
return DataTools.uploadCover(Track.class, id, fileName, fileInputStream, fileMetaData);
public Track uploadCover(@PathParam("id") final Long id, @FormDataParam("uri") final String uri, @FormDataParam("file") final InputStream fileInputStream,
@FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
if (uri != null) {
DataTools.uploadCoverFromUri(this.da, Track.class, id, uri);
} else {
DataTools.uploadCover(this.da, Track.class, id, fileInputStream, fileMetaData);
}
return this.da.get(Track.class, id);
}
@DELETE
@Path("{id}/cover/{coverId}")
@RolesAllowed("ADMIN")
public Track removeCover(@PathParam("id") final Long id, @PathParam("coverId") final UUID coverId) throws Exception {
AddOnDataJson.removeLink(Track.class, id, "covers", coverId);
return DataAccess.get(Track.class, id);
AddOnDataJson.removeLink(this.da, Track.class, id, "covers", coverId);
return this.da.get(Track.class, id);
}
@POST
@Path("upload/")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
// Formatter:off
@AsyncType(Track.class)
@TypeScriptProgress
public Response uploadTrack(
@FormDataParam("fileName") String fileName, //
@FormDataParam("gender") String gender, //
@FormDataParam("artist") String artist, //
//@FormDataParam("seriesId") String seriesId, Not used ...
@FormDataParam("album") String album, //
@AsyncType(Long.class) @FormDataParam("trackId") String trackId, //
public Response uploadTrack( //
@FormDataParam("title") String title, //
@FormDataOptional @AsyncType(Long.class) @FormDataParam("genderId") String genderId, //
@FormDataOptional @AsyncType(Long.class) @FormDataParam("artistId") String artistId, //
@FormDataOptional @AsyncType(Long.class) @FormDataParam("albumId") String albumId, //
@FormDataOptional @AsyncType(Long.class) @FormDataParam("trackId") String trackId, //
@FormDataParam("file") final InputStream fileInputStream, //
@FormDataParam("file") final FormDataContentDisposition fileMetaData //
) {
// Formatter:on
try {
// correct input string stream :
fileName = DataTools.multipartCorrection(fileName);
gender = DataTools.multipartCorrection(gender);
artist = DataTools.multipartCorrection(artist);
album = DataTools.multipartCorrection(album);
trackId = DataTools.multipartCorrection(trackId);
albumId = DataTools.multipartCorrection(albumId);
artistId = DataTools.multipartCorrection(artistId);
genderId = DataTools.multipartCorrection(genderId);
title = DataTools.multipartCorrection(title);
//public NodeSmall uploadFile(final FormDataMultiPart form) {
// public NodeSmall uploadFile(final FormDataMultiPart form) {
LOGGER.info("Upload media file: " + fileMetaData);
LOGGER.info(" > fileName: " + fileName);
LOGGER.info(" > gender: " + gender);
LOGGER.info(" > artist: " + artist);
LOGGER.info(" > album: " + album);
LOGGER.info(" > genderId: " + genderId);
LOGGER.info(" > artistId: " + artistId);
LOGGER.info(" > albumId: " + albumId);
LOGGER.info(" > trackId: " + trackId);
LOGGER.info(" > title: " + title);
LOGGER.info(" > fileInputStream: " + fileInputStream);
LOGGER.info(" > fileMetaData: " + fileMetaData);
/*
if (typeId == null) {
return Response.status(406).
entity("Missong Input 'type'").
type("text/plain").
build();
}
*/
/* if (typeId == null) { return Response.status(406). entity("Missong Input 'type'"). type("text/plain"). build(); } */
final long tmpUID = DataTools.getTmpDataId();
final String sha512 = DataTools.saveTemporaryFile(fileInputStream, tmpUID);
Data data = DataTools.getWithSha512(sha512);
Data data = DataTools.getWithSha512(this.da, sha512);
if (data == null) {
LOGGER.info("Need to add the data in the BDD ... ");
try {
data = DataTools.createNewData(tmpUID, fileName, sha512);
data = DataTools.createNewData(this.da, tmpUID, fileMetaData.getFileName(), sha512);
} catch (final IOException ex) {
DataTools.removeTemporaryFile(tmpUID);
ex.printStackTrace();
@ -180,73 +170,26 @@ public class TrackResource {
}
} else if (data.deleted) {
LOGGER.info("Data already exist but deleted");
DataTools.undelete(data.id);
DataTools.undelete(this.da, data.uuid);
data.deleted = false;
} else {
LOGGER.info("Data already exist ... all good");
}
// Fist step: retrieve all the Id of each parents:...
LOGGER.info("Find typeNode");
Gender genderElem = null;
if (gender != null) {
genderElem = DataAccess.getWhere(Gender.class, new Condition(new QueryCondition("name", "=", gender)));
if (genderElem == null) {
genderElem = new Gender();
genderElem.name = gender;
genderElem = DataAccess.insert(genderElem);
}
}
// NodeSmall typeNode = TypeResource.getWithId(Long.parseLong(typeId));
// if (typeNode == null) {
// DataTools.removeTemporaryFile(tmpUID);
// return Response.notModified("TypeId does not exist ...").build();
// }
LOGGER.info(" ==> genderElem={}", genderElem);
Artist artistElem = null;
if (artist != null) {
LOGGER.info(" Try to find Artist: '{}'", artist);
artistElem = DataAccess.getWhere(Artist.class, new Condition(new QueryCondition("name", "=", artist)));
if (artistElem == null) {
LOGGER.info(" ** Create a new one...");
artistElem = new Artist();
artistElem.name = artist;
artistElem = DataAccess.insert(artistElem);
}
}
LOGGER.info(" ==> artistElem={}", artistElem);
Album albumElem = null;
if (album != null) {
albumElem = DataAccess.getWhere(Album.class, new Condition(new QueryCondition("name", "=", album)));
if (albumElem == null) {
albumElem = new Album();
albumElem.name = album;
albumElem = DataAccess.insert(albumElem);
}
}
LOGGER.info(" ==> " + album);
LOGGER.info("add media");
Track trackElem = new Track();
trackElem.name = title;
trackElem.track = trackId != null ? Long.parseLong(trackId) : null;
trackElem.albumId = albumElem != null ? albumElem.id : null;
trackElem.genderId = genderElem != null ? genderElem.id : null;
trackElem.dataId = data.id;
trackElem.albumId = albumId != null ? Long.parseLong(albumId) : null;
trackElem.genderId = genderId != null ? Long.parseLong(genderId) : null;
trackElem.dataId = data.uuid;
// Now list of artist has an internal management:
if (artistElem != null) {
if (artistId != null) {
trackElem.artists = new ArrayList<>();
trackElem.artists.add(artistElem.id);
trackElem.artists.add(artistId != null ? Long.parseLong(artistId) : null);
}
trackElem = DataAccess.insert(trackElem);
/*
Old mode of artist insertion (removed due to the slowlest request of getting value
if (artistElem != null) {
DataAccess.addLink(Track.class, trackElem.id, "artist", artistElem.id);
}
*/
trackElem = this.da.insert(trackElem, new CheckFunction(CHECKER));
/* Old mode of artist insertion (removed due to the slowlest request of getting value if (artistElem != null) { this.dam.addLink(Track.class, trackElem.id, "artist", artistElem.id); } */
return Response.ok(trackElem).build();
} catch (final Exception ex) {
LOGGER.info("Catch an unexpected error ... {}", ex.getMessage());

View File

@ -1,9 +1,12 @@
package org.kar.karusic.api;
import java.util.List;
import java.util.Map;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.filter.GenericContext;
import org.kar.karusic.api.UserResourceModel.PartRight;
import org.kar.karusic.api.UserResourceModel.UserMe;
import org.kar.karusic.model.UserKarusic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -20,9 +23,10 @@ import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.SecurityContext;
@Path("/users")
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Produces(MediaType.APPLICATION_JSON)
public class UserResource {
private static final Logger LOGGER = LoggerFactory.getLogger(UserResource.class);
private final DataAccess da = DataAccess.createInterface();
@JsonInclude(JsonInclude.Include.NON_NULL)
public class UserOut {
@ -44,7 +48,7 @@ public class UserResource {
public List<UserKarusic> gets() {
LOGGER.info("getUsers");
try {
return DataAccess.gets(UserKarusic.class);
return this.da.gets(UserKarusic.class);
} catch (final Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
@ -63,7 +67,7 @@ public class UserResource {
LOGGER.info("== USER {} ", gc.userByToken.name);
LOGGER.info("===================================================");
try {
return DataAccess.get(UserKarusic.class, userId);
return this.da.get(UserKarusic.class, userId);
} catch (final Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
@ -74,10 +78,15 @@ public class UserResource {
@GET
@Path("me")
@RolesAllowed("USER")
public UserOut getMe(@Context final SecurityContext sc) {
public UserMe getMe(@Context final SecurityContext sc) {
LOGGER.debug("getMe()");
final GenericContext gc = (GenericContext) sc.getUserPrincipal();
LOGGER.debug("== USER ? {}", gc.userByToken);
return new UserOut(gc.userByToken.id, gc.userByToken.name);
return new UserMe(gc.userByToken.id, gc.userByToken.name, //
Map.of(gc.userByToken.name, //
Map.of("admin", PartRight.READ_WRITE, //
"user", PartRight.READ_WRITE), //
"karusic", //
Map.of("user", PartRight.READ)));
}
}

View File

@ -0,0 +1,12 @@
package org.kar.karusic.api.UserResourceModel;
import java.util.HashMap;
import org.kar.archidata.annotation.NoWriteSpecificMode;
@NoWriteSpecificMode
public class ModuleAuthorizations extends HashMap<String, PartRight> {
private static final long serialVersionUID = 1L;
public ModuleAuthorizations() {}
}

View File

@ -0,0 +1,29 @@
package org.kar.karusic.api.UserResourceModel;
import com.fasterxml.jackson.annotation.JsonValue;
public enum PartRight {
READ(1), //
WRITE(2), //
READ_WRITE(3);
private final int value;
PartRight(final int value) {
this.value = value;
}
@JsonValue
public int getValue() {
return this.value;
}
public static PartRight fromValue(final int value) {
for (final PartRight species : PartRight.values()) {
if (species.getValue() == value) {
return species;
}
}
throw new IllegalArgumentException("PartRight: Unknown value: " + value);
}
}

View File

@ -0,0 +1,24 @@
package org.kar.karusic.api.UserResourceModel;
import java.util.Map;
import org.kar.archidata.annotation.NoWriteSpecificMode;
import io.swagger.v3.oas.annotations.media.Schema;
@NoWriteSpecificMode
public class UserMe {
public long id;
public String login;
@Schema(description = "Map<EntityName, Map<PartName, Right>>")
public Map<String, Map<String, PartRight>> rights;
public UserMe() {}
public UserMe(final long id, final String login, final Map<String, Map<String, PartRight>> rights) {
this.id = id;
this.login = login;
this.rights = rights;
}
}

View File

@ -4,57 +4,57 @@ package org.kar.karusic.internal;
//import io.scenarium.logger.Logger;
public class Log {
// private static final String LIB_NAME = "logger";
// private static final String LIB_NAME_DRAW = Logger.getDrawableName(LIB_NAME);
// private static final boolean PRINT_CRITICAL = Logger.getNeedPrint(LIB_NAME, LogLevel.CRITICAL);
// private static final boolean PRINT_ERROR = Logger.getNeedPrint(LIB_NAME, LogLevel.ERROR);
// private static final boolean PRINT_WARNING = Logger.getNeedPrint(LIB_NAME, LogLevel.WARNING);
// private static final boolean PRINT_INFO = Logger.getNeedPrint(LIB_NAME, LogLevel.INFO);
// private static final boolean PRINT_DEBUG = Logger.getNeedPrint(LIB_NAME, LogLevel.DEBUG);
// private static final boolean PRINT_VERBOSE = Logger.getNeedPrint(LIB_NAME, LogLevel.VERBOSE);
// private static final boolean PRINT_TODO = Logger.getNeedPrint(LIB_NAME, LogLevel.TODO);
// private static final boolean PRINT_PRINT = Logger.getNeedPrint(LIB_NAME, LogLevel.PRINT);
//
// private Log() {}
//
// public static void print(String data) {
// if (PRINT_PRINT)
// Logger.print(LIB_NAME_DRAW, data);
// }
//
// public static void todo(String data) {
// if (PRINT_TODO)
// Logger.todo(LIB_NAME_DRAW, data);
// }
//
// public static void critical(String data) {
// if (PRINT_CRITICAL)
// Logger.critical(LIB_NAME_DRAW, data);
// }
//
// public static void error(String data) {
// if (PRINT_ERROR)
// Logger.error(LIB_NAME_DRAW, data);
// }
//
// public static void warning(String data) {
// if (PRINT_WARNING)
// Logger.warning(LIB_NAME_DRAW, data);
// }
//
// public static void info(String data) {
// if (PRINT_INFO)
// Logger.info(LIB_NAME_DRAW, data);
// }
//
// public static void debug(String data) {
// if (PRINT_DEBUG)
// Logger.debug(LIB_NAME_DRAW, data);
// }
//
// public static void verbose(String data) {
// if (PRINT_VERBOSE)
// Logger.verbose(LIB_NAME_DRAW, data);
// }
// private static final String LIB_NAME = "logger";
// private static final String LIB_NAME_DRAW = Logger.getDrawableName(LIB_NAME);
// private static final boolean PRINT_CRITICAL = Logger.getNeedPrint(LIB_NAME, LogLevel.CRITICAL);
// private static final boolean PRINT_ERROR = Logger.getNeedPrint(LIB_NAME, LogLevel.ERROR);
// private static final boolean PRINT_WARNING = Logger.getNeedPrint(LIB_NAME, LogLevel.WARNING);
// private static final boolean PRINT_INFO = Logger.getNeedPrint(LIB_NAME, LogLevel.INFO);
// private static final boolean PRINT_DEBUG = Logger.getNeedPrint(LIB_NAME, LogLevel.DEBUG);
// private static final boolean PRINT_VERBOSE = Logger.getNeedPrint(LIB_NAME, LogLevel.VERBOSE);
// private static final boolean PRINT_TODO = Logger.getNeedPrint(LIB_NAME, LogLevel.TODO);
// private static final boolean PRINT_PRINT = Logger.getNeedPrint(LIB_NAME, LogLevel.PRINT);
//
// private Log() {}
//
// public static void print(String data) {
// if (PRINT_PRINT)
// Logger.print(LIB_NAME_DRAW, data);
// }
//
// public static void todo(String data) {
// if (PRINT_TODO)
// Logger.todo(LIB_NAME_DRAW, data);
// }
//
// public static void critical(String data) {
// if (PRINT_CRITICAL)
// Logger.critical(LIB_NAME_DRAW, data);
// }
//
// public static void error(String data) {
// if (PRINT_ERROR)
// Logger.error(LIB_NAME_DRAW, data);
// }
//
// public static void warning(String data) {
// if (PRINT_WARNING)
// Logger.warning(LIB_NAME_DRAW, data);
// }
//
// public static void info(String data) {
// if (PRINT_INFO)
// Logger.info(LIB_NAME_DRAW, data);
// }
//
// public static void debug(String data) {
// if (PRINT_DEBUG)
// Logger.debug(LIB_NAME_DRAW, data);
// }
//
// public static void verbose(String data) {
// if (PRINT_VERBOSE)
// Logger.verbose(LIB_NAME_DRAW, data);
// }
}

View File

@ -2,6 +2,7 @@ package org.kar.karusic.migration;
import java.util.List;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.migration.MigrationSqlStep;
import org.kar.archidata.model.Data;
import org.kar.archidata.model.User;
@ -10,11 +11,14 @@ import org.kar.karusic.model.Artist;
import org.kar.karusic.model.Gender;
import org.kar.karusic.model.Playlist;
import org.kar.karusic.model.Track;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Initialization extends MigrationSqlStep {
private static final Logger LOGGER = LoggerFactory.getLogger(Initialization.class);
public static final int KARSO_INITIALISATION_ID = 1;
public static final List<Class<?>> CLASSES_BASE = List.of(Album.class,Artist.class,Data.class,Gender.class,Playlist.class,Track.class,User.class);
public static final List<Class<?>> CLASSES_BASE = List.of(Album.class, Artist.class, Data.class, Gender.class, Playlist.class, Track.class, User.class);
@Override
public String getName() {
@ -31,35 +35,35 @@ public class Initialization extends MigrationSqlStep {
addClass(elem);
}
addAction("""
INSERT INTO `gender` (`id`, `name`, `description`) VALUES
(1, 'Variété française', NULL),
(2, 'Pop', NULL),
(3, 'inconnue', NULL),
(4, 'Disco', NULL),
(5, 'Enfants', NULL),
(6, 'Portugaise', NULL),
(7, 'Apprentissage', NULL),
(8, 'Blues', NULL),
(9, 'Jazz', NULL),
(10, 'Chanson Noël', NULL),
(11, 'DubStep', NULL),
(12, 'Rap français', NULL),
(13, 'Classique', NULL),
(14, 'Rock', NULL),
(15, 'Electro', NULL),
(16, 'Celtique', NULL),
(17, 'Country', NULL),
(18, 'Variété Québéquoise', NULL),
(19, 'Médiéval', NULL),
(20, 'Variété Italienne', NULL),
(21, 'Comédie Musicale', NULL),
(22, 'Vianney', NULL),
(23, 'Bande Original', NULL),
(24, 'Bande Originale', NULL),
(25, 'Variété Belge', NULL),
(26, 'Gospel', NULL);
""");
addAction((final DataAccess da) -> {
final List<Gender> data = List.of(//
new Gender(1L, "Variété française"), //
new Gender(2L, "Pop"), //
new Gender(3L, "inconnue"), //
new Gender(4L, "Disco"), //
new Gender(5L, "Enfants"), //
new Gender(6L, "Portugaise"), //
new Gender(7L, "Apprentissage"), //
new Gender(8L, "Blues"), //
new Gender(9L, "Jazz"), //
new Gender(10L, "Chanson Noël"), //
new Gender(11L, "DubStep"), //
new Gender(12L, "Rap français"), //
new Gender(13L, "Classique"), //
new Gender(14L, "Rock"), //
new Gender(15L, "Electro"), //
new Gender(16L, "Celtique"), //
new Gender(17L, "Country"), //
new Gender(18L, "Variété Québéquoise"), //
new Gender(19L, "Médiéval"), //
new Gender(20L, "Variété Italienne"), //
new Gender(21L, "Comédie Musicale"), //
new Gender(22L, "Vianney"), //
new Gender(23L, "Bande Original"), //
new Gender(24L, "Bande Originale"), //
new Gender(25L, "Variété Belge"), //
new Gender(26L, "Gospel"));
});
// set start increment element to permit to add after default elements
addAction("""
ALTER TABLE `album` AUTO_INCREMENT = 1000;
@ -81,4 +85,25 @@ public class Initialization extends MigrationSqlStep {
""", "mysql");
}
public static void dropAll(final DataAccess da) {
for (final Class<?> element : CLASSES_BASE) {
try {
da.drop(element);
} catch (final Exception ex) {
LOGGER.error("Fail to drop table !!!!!!");
ex.printStackTrace();
}
}
}
public static void cleanAll(final DataAccess da) {
for (final Class<?> element : CLASSES_BASE) {
try {
da.cleanAll(element);
} catch (final Exception ex) {
LOGGER.error("Fail to clean table !!!!!!");
ex.printStackTrace();
}
}
}
}

View File

@ -9,7 +9,7 @@ import java.util.UUID;
import org.kar.archidata.api.DataResource;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.addOn.model.LinkTable;
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;
@ -41,10 +41,10 @@ public class Migration20240226 extends MigrationSqlStep {
""");
addAction(() -> {
final List<UUIDConversion> datas = DataAccess.gets(UUIDConversion.class, new AccessDeletedItems(), new OverrideTableName("data"));
for (final UUIDConversion elem: datas) {
for (final UUIDConversion elem : datas) {
elem.uuid = UuidUtils.nextUUID();
}
for (final UUIDConversion elem: datas) {
for (final UUIDConversion elem : datas) {
DataAccess.update(elem, elem.id, List.of("uuid"), new OverrideTableName("data"));
}
});
@ -52,18 +52,18 @@ public class Migration20240226 extends MigrationSqlStep {
ALTER TABLE `data` CHANGE `uuid` `uuid` binary(16) DEFAULT (UUID_TO_BIN(UUID(), TRUE));
""");
final List<String> tableToTransform = List.of("album", "artist", "gender", "playlist", "track", "user");
for (final String tableName : tableToTransform ) {
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<LinkTable> links = DataAccess.gets(LinkTable.class, new OverrideTableName(tableName + "_link_cover"));
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) {
for (final CoverConversion media : medias) {
final List<UUID> values = new ArrayList<>();
for (final LinkTable link: links) {
for (final LinkTableLongLong link : links) {
if (link.object1Id.equals(media.id)) {
for (final UUIDConversion data: datas) {
for (final UUIDConversion data : datas) {
if (data.id.equals(link.object2Id)) {
values.add(data.uuid);
break;
@ -87,8 +87,8 @@ public class Migration20240226 extends MigrationSqlStep {
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("track"));
for (final MediaConversion media: medias) {
for (final UUIDConversion data: datas) {
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("track"));
@ -113,7 +113,7 @@ public class Migration20240226 extends MigrationSqlStep {
// Move the files...
addAction(() -> {
final List<UUIDConversion> datas = DataAccess.gets(UUIDConversion.class, new AccessDeletedItems(), new OverrideTableName("data"));
for (final UUIDConversion data: datas) {
for (final UUIDConversion data : datas) {
final String origin = DataResource.getFileDataOld(data.id);
final String destination = DataResource.getFileData(data.uuid);
LOGGER.info("move file = {}", origin);

View File

@ -0,0 +1,34 @@
package org.kar.karusic.migration;
import org.kar.archidata.migration.MigrationSqlStep;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Migration20240907 extends MigrationSqlStep {
private static final Logger LOGGER = LoggerFactory.getLogger(Migration20240907.class);
public static final int KARSO_INITIALISATION_ID = 1;
@Override
public String getName() {
return "migration-2024-09-07: convert data id in uuid";
}
public Migration20240907() {
}
@Override
public void generateStep() throws Exception {
addAction("""
ALTER TABLE `data` DROP INDEX `PRIMARY`;
""");
addAction("""
ALTER TABLE `data` CHANGE `id` `uuid` binary(16) DEFAULT (UUID_TO_BIN(UUID(), TRUE));
""");
addAction("""
ALTER TABLE `data` ADD PRIMARY KEY `uuid` (`uuid`);
""");
}
}

View File

@ -1,16 +1,41 @@
package org.kar.karusic.model;
import java.time.LocalDate;
import java.util.List;
import java.util.UUID;
import org.kar.archidata.annotation.DataIfNotExists;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.dataAccess.options.CheckJPA;
import org.kar.archidata.model.Data;
import org.kar.archidata.model.GenericDataSoftDelete;
import com.fasterxml.jackson.annotation.JsonInclude;
import dev.morphia.annotations.Entity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Nullable;
import jakarta.persistence.Column;
import jakarta.persistence.Table;
@Entity("Album")
@Table(name = "album")
@DataIfNotExists
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Album extends NodeSmall {
public class Album extends GenericDataSoftDelete {
public static class AlbumChecker extends CheckJPA<Album> {
public AlbumChecker() {
super(Album.class);
}
}
@Column(length = 256)
public String name = null;
@Column(length = 0)
public String description = null;
@Schema(description = "List of Id of the specific covers")
@DataJson(targetEntity = Data.class)
@Nullable
public List<UUID> covers = null;
public LocalDate publication = null;
}

View File

@ -1,18 +1,42 @@
package org.kar.karusic.model;
import java.time.LocalDate;
import java.util.List;
import java.util.UUID;
import org.kar.archidata.annotation.DataIfNotExists;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.dataAccess.options.CheckJPA;
import org.kar.archidata.model.Data;
import org.kar.archidata.model.GenericDataSoftDelete;
import com.fasterxml.jackson.annotation.JsonInclude;
import dev.morphia.annotations.Entity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Nullable;
import jakarta.persistence.Column;
import jakarta.persistence.Table;
@Entity("Artist")
@Table(name = "artist")
@DataIfNotExists
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Artist extends NodeSmall {
public class Artist extends GenericDataSoftDelete {
public static class ArtistChecker extends CheckJPA<Artist> {
public ArtistChecker() {
super(Artist.class);
}
}
@Column(length = 256)
public String name = null;
@Column(length = 0)
public String description = null;
@Schema(description = "List of Id of the specific covers")
@DataJson(targetEntity = Data.class)
@Nullable
public List<UUID> covers = null;
@Column(length = 256)
public String firstName = null;
@Column(length = 256)

View File

@ -12,15 +12,48 @@ CREATE TABLE `node` (
) AUTO_INCREMENT=10;
*/
import java.util.List;
import java.util.UUID;
import org.kar.archidata.annotation.DataIfNotExists;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.dataAccess.options.CheckJPA;
import org.kar.archidata.model.Data;
import org.kar.archidata.model.GenericDataSoftDelete;
import com.fasterxml.jackson.annotation.JsonInclude;
import dev.morphia.annotations.Entity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Nullable;
import jakarta.persistence.Column;
import jakarta.persistence.Table;
@Entity("Gender")
@Table(name = "gender")
@DataIfNotExists
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Gender extends NodeSmall {
public class Gender extends GenericDataSoftDelete {
public static class GenderChecker extends CheckJPA<Gender> {
public GenderChecker() {
super(Gender.class);
}
}
@Column(length = 256)
public String name = null;
@Column(length = 0)
public String description = null;
@Schema(description = "List of Id of the specific covers")
@DataJson(targetEntity = Data.class)
@Nullable
public List<UUID> covers = null;
public Gender() {}
public Gender(final Long id, final String name) {
this.id = id;
this.name = name;
}
}

View File

@ -1,36 +0,0 @@
package org.kar.karusic.model;
/*
CREATE TABLE `node` (
`id` bigint NOT NULL COMMENT 'table ID' AUTO_INCREMENT PRIMARY KEY,
`deleted` BOOLEAN NOT NULL DEFAULT false,
`create_date` datetime NOT NULL DEFAULT now() COMMENT 'Time the element has been created',
`modify_date` datetime NOT NULL DEFAULT now() COMMENT 'Time the element has been update',
`type` enum("TYPE", "UNIVERS", "SERIE", "SAISON", "MEDIA") NOT NULL DEFAULT 'TYPE',
`name` TEXT COLLATE 'utf8_general_ci' NOT NULL,
`description` TEXT COLLATE 'utf8_general_ci',
`parent_id` bigint
) AUTO_INCREMENT=10;
*/
import java.util.List;
import java.util.UUID;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.model.Data;
import org.kar.archidata.model.GenericDataSoftDelete;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class NodeSmall extends GenericDataSoftDelete {
@Column(length = 256)
public String name = null;
@Column(length = 0)
public String description = null;
@Schema(description = "List of Id of the specific covers")
@DataJson(targetEntity = Data.class)
public List<UUID> covers = null;
}

View File

@ -13,19 +13,43 @@ CREATE TABLE `node` (
*/
import java.util.List;
import java.util.UUID;
import org.kar.archidata.annotation.DataIfNotExists;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.dataAccess.options.CheckJPA;
import org.kar.archidata.model.Data;
import org.kar.archidata.model.GenericDataSoftDelete;
import com.fasterxml.jackson.annotation.JsonInclude;
import dev.morphia.annotations.Entity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Nullable;
import jakarta.persistence.Column;
import jakarta.persistence.FetchType;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.Table;
@Entity("Playlist")
@Table(name = "playlist")
@DataIfNotExists
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Playlist extends NodeSmall {
public class Playlist extends GenericDataSoftDelete {
public static class PlaylistChecker extends CheckJPA<Playlist> {
public PlaylistChecker() {
super(Playlist.class);
}
}
@Column(length = 256)
public String name = null;
@Column(length = 0)
public String description = null;
@Schema(description = "List of Id of the specific covers")
@DataJson(targetEntity = Data.class)
@Nullable
public List<UUID> covers = null;
@ManyToMany(fetch = FetchType.LAZY, targetEntity = Track.class)
public List<Long> tracks = null;
}

View File

@ -17,21 +17,43 @@ import java.util.UUID;
import org.kar.archidata.annotation.DataIfNotExists;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.dataAccess.options.CheckJPA;
import org.kar.archidata.model.Data;
import org.kar.archidata.model.GenericDataSoftDelete;
import com.fasterxml.jackson.annotation.JsonInclude;
import dev.morphia.annotations.Entity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Nullable;
import jakarta.persistence.Column;
import jakarta.persistence.Table;
@Entity("Track")
@Table(name = "track")
@DataIfNotExists
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Track extends NodeSmall {
public class Track extends GenericDataSoftDelete {
public static class TrackChecker extends CheckJPA<Track> {
public TrackChecker() {
super(Track.class);
}
}
@Column(length = 256)
public String name = null;
@Column(length = 0)
public String description = null;
@Schema(description = "List of Id of the specific covers")
@DataJson(targetEntity = Data.class)
@Nullable
public List<UUID> covers = null;
public Long genderId = null;
public Long albumId = null;
public Long track = null;
public UUID dataId = null;
//@ManyToMany(fetch = FetchType.LAZY, targetEntity = Artist.class)
// @ManyToMany(fetch = FetchType.LAZY, targetEntity = Artist.class)
@DataJson
@Column(length = 0)
public List<Long> artists = null;

View File

@ -3,7 +3,6 @@ package org.kar.karusic.util;
public class ConfigVariable {
public static final String BASE_NAME = "ORG_KARUSIC_";
public static String getFrontFolder() {
String out = System.getenv(BASE_NAME + "FRONT_FOLDER");
if (out == null) {

View File

@ -3,12 +3,14 @@
# Default logging detail level for all instances of SimpleLogger.
# Must be one of ("trace", "debug", "info", "warn", or "error").
# If not specified, defaults to "info".
org.slf4j.simpleLogger.defaultLogLevel=trace
org.slf4j.simpleLogger.defaultLogLevel=INFO
# Logging detail level for a SimpleLogger instance named "xxxxx".
# Must be one of ("trace", "debug", "info", "warn", or "error").
# If not specified, the default logging detail level is used.
#org.slf4j.simpleLogger.log.xxxxx=
org.slf4j.simpleLogger.log.org.kar.archidata=TRACE
org.slf4j.simpleLogger.log.org.kar.karusic=TRACE
# Set to true if you want the current date and time to be included in output messages.
# Default is false, and will output the number of milliseconds elapsed since startup.

View File

@ -0,0 +1,10 @@
package test.kar.karusic;
import java.util.Map;
import org.kar.archidata.tools.JWTWrapper;
public class Common {
static String USER_TOKEN = JWTWrapper.createJwtTestToken(16512, "test_user_login", "KarAuth", "karusic", Map.of("karusic", Map.of("USER", Boolean.TRUE)));
static String ADMIN_TOKEN = JWTWrapper.createJwtTestToken(16512, "test_admin_login", "KarAuth", "karusic", Map.of("karusic", Map.of("USER", Boolean.TRUE, "ADMIN", Boolean.TRUE)));
}

View File

@ -1,51 +1,43 @@
package test.kar.karusic;
import java.util.Map;
import java.util.UUID;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.MethodOrderer;
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.exception.RESTErrorResponseExeption;
import org.kar.archidata.model.GetToken;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.kar.archidata.tools.JWTWrapper;
import org.kar.archidata.tools.RESTApi;
import org.kar.karusic.api.HealthCheck.HealthResult;
import org.kar.karusic.migration.Initialization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.nimbusds.jwt.JWTClaimsSet;
@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;
public void login(final String login, final String password) {
try {
final GetToken token = api.post(GetToken.class, "users/get_token", DataGetToken.generate(login, "v1", "202515252", password));
api.setToken(token.jwt());
} catch (final Exception ex) {
Assertions.fail("Can not get Authentication for '" + login + "' ==> " + ex.getMessage());
}
}
public void loginAdmin() {
login("karadmin", "adminA@666");
}
private static UUID idTest;
@BeforeAll
public static void configureWebServer() throws Exception {
LOGGER.info("configure server ...");
webInterface = new WebLauncherTest();
LOGGER.info("Clean previous table");
try {
Initialization.dropAll();
} catch (final Exception ex) {
ex.printStackTrace();
LOGGER.error("plop: {}", ex.getLocalizedMessage());
throw ex;
}
LOGGER.info("Create DB");
try {
webInterface.migrateDB();
@ -57,194 +49,18 @@ public class TestBase {
webInterface.process();
LOGGER.info("Start REST (DONE)");
api = new RESTApi(ConfigBaseVariable.apiAdress);
api.setToken(Common.USER_TOKEN);
}
@AfterAll
public static void stopWebServer() throws InterruptedException {
public static void stopWebServer() throws Exception {
LOGGER.info("Kill the web server");
webInterface.stop();
webInterface = null;
// TODO: do it better...
}
@Order(1)
@Test
//@RepeatedTest(10)
public void checkHealthCheck() throws Exception {
final HealthResult result = api.get(HealthResult.class, "health_check");
Assertions.assertEquals(result.value(), "alive and kicking");
}
@Order(2)
@Test
public void checkHealthCheckWrongAPI() throws Exception {
Assertions.assertThrows(RESTErrorResponseExeption.class, () -> api.get(HealthResult.class, "health_checks"));
}
@Order(3)
@Test
public void firstUserConnect() throws Exception {
final GetToken result = api.post(GetToken.class, "users/get_token", DataGetToken.generate("karadmin", "v1", "202515252", "adminA@666"));
final String[] splitted = result.jwt().split("\\.");
Assertions.assertEquals(3, splitted.length);
final String authorization = result.jwt();
LOGGER.debug(" validate token : " + authorization);
// Note with local access we get the internal key of the system.
final JWTClaimsSet ret = JWTWrapper.validateToken(authorization, "KarAuth", null);
// check the token is valid !!! (signed and coherent issuer...
Assertions.assertNotNull(ret);
// check userID
final String userUID = ret.getSubject();
final long id = Long.parseLong(userUID);
Assertions.assertEquals(1, id);
final String name = (String) ret.getClaim("login");
Assertions.assertEquals("karadmin", name);
final Object rowRight = ret.getClaim("right");
Assertions.assertNotNull(rowRight);
final Map<String, Map<String, Object>> rights = (Map<String, Map<String, Object>>) ret.getClaim("right");
// Check if the element contain the basic keys:
Assertions.assertEquals(rights.size(), 1);
Assertions.assertTrue(rights.containsKey("karusic"));
final Map<String, Object> applRight = rights.get("karusic");
//logger.error("full right: {}", applRight);
Assertions.assertEquals(applRight.size(), 2);
Assertions.assertTrue(applRight.containsKey("ADMIN"));
Assertions.assertEquals(true, applRight.get("ADMIN"));
Assertions.assertTrue(applRight.containsKey("USER"));
Assertions.assertEquals(true, applRight.get("USER"));
//logger.debug("request user: '{}' right: '{}' row='{}'", userUID, applRight, rowRight);
//Assertions.assertEquals("eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9", splitted[0]);
//Assertions.assertEquals("eyJzdWIiOiIwIiwiYXBwbGljYXRpb24iOiJrYXJzbyIsImlzcyI6IkthckF1dGgiLCJyaWdodCI6eyJrYXJzbyI6eyJBRE1JTiI6dHJ1ZSwiVVNFUiI6dHJ1ZX19LCJsb2dpbiI6ImthcmFkbWluIiwiZXhwIjoxNjg0MTk5MTkzLCJpYXQiOjE2ODI3NTU0MjV9", splitted[1]);
// TODO ... Assertions.assertEquals("????", splitted[2]);
}
public void checkFail(final String type, final String urlOffset, final int errorStatus) {
checkFail(type, urlOffset, errorStatus, null);
}
public void checkFail(final String type, final String urlOffset, final int errorStatus, final String data) {
LOGGER.info("Test API: url={} urlOffset={}", type, urlOffset);
try {
if ("GET".equals(type)) {
api.get(String.class, urlOffset);
} else if ("POST".equals(type)) {
api.post(String.class, urlOffset, data);
} else if ("PUT".equals(type)) {
api.put(String.class, urlOffset, data);
} else if ("DELETE".equals(type)) {
api.delete(String.class, urlOffset);
}
Assertions.fail("Request on URL does not fail as expected: '" + type + "' url='" + urlOffset + "'");
} catch (final RESTErrorResponseExeption ex) {
if (errorStatus != ex.status) {
LOGGER.error("Fail in test with the wrong return errors: {}", ex.toString());
}
Assertions.assertEquals(errorStatus, ex.status);
} catch (final Exception ex) {
LOGGER.error("Unexpected throw error: {}", ex);
Assertions.fail("Unexpected throws...");
}
}
public void checkWork(final String type, final String urlOffset) {
checkWork(type, urlOffset, null);
}
public void checkWork(final String type, final String urlOffset, final String data) {
LOGGER.info("Test API: url={} urlOffset={}", type, urlOffset);
try {
if ("GET".equals(type)) {
api.get(String.class, urlOffset);
} else if ("POST".equals(type)) {
api.post(String.class, urlOffset, data);
} else if ("PUT".equals(type)) {
api.put(String.class, urlOffset, data);
} else if ("DELETE".equals(type)) {
api.delete(String.class, urlOffset);
}
//Assertions.fail("Request on URL does not fail as expected: '" + type + "' url='" + urlOffset + "'");
} catch (final RESTErrorResponseExeption ex) {
Assertions.fail("Must not fail ... " + ex.toString());
} catch (final Exception ex) {
LOGGER.error("Unexpected throw error: {}", ex);
Assertions.fail("Unexpected throws...");
}
}
@Order(4)
@Test
public void checkUnAuthorizedAPI() throws Exception {
// /application/
checkFail("GET", "application/", 401);
checkFail("POST", "application/", 401, "{}");
checkFail("PUT", "application/", 405, "{}"); // does not exist
checkFail("DELETE", "application/", 405); // does not exist
// /application/{id}
checkFail("GET", "application/0", 401);
checkFail("PUT", "application/0", 401, "{}");
checkFail("POST", "application/0", 405, "{}");
checkFail("DELETE", "application/0", 401);
// /application/{id}/*
checkFail("GET", "application/0/users", 401);
// /application/*
checkFail("GET", "application/small", 401);
checkFail("GET", "application/get_token", 401);
checkFail("GET", "application/return", 401);
// /application_token/ section:
checkFail("GET", "application_token/0", 401);
checkFail("DELETE", "application_token/0/5", 401);
checkFail("DELETE", "application_token/0/create", 401);
// /front/*
checkFail("GET", "front", 404); // no index in test section
// health check
checkWork("GET", "health_check");
// public_key (only application)
checkFail("GET", "public_key", 401);
checkFail("GET", "public_key/pem", 401);
// /right
checkFail("GET", "right", 401);
checkFail("POST", "right", 401, "{}");
checkFail("GET", "right/0", 401);
checkFail("PUT", "right/0", 401, "{}");
checkFail("DELETE", "right/0", 401);
// /system_config
checkWork("GET", "system_config/is_sign_up_availlable");
checkFail("GET", "system_config/key/skjdfhkjsdhfkjsh", 401);
checkFail("PUT", "system_config/key/skjdfhkjsdhfkjsh", 401, "{}");
// /users
checkFail("GET", "users", 401);
checkFail("GET", "users/0", 401);
checkFail("POST", "users/0/application/0/link", 401, "{}");
checkFail("POST", "users/0/set_admin", 401, "{}");
checkFail("POST", "users/0/set_blocked", 401, "{}");
checkFail("POST", "users/create_new_user", 401, "{}");
checkFail("GET", "users/me", 401, "{}");
checkFail("POST", "users/password", 401, "{}");
checkWork("GET", "users/check_login?login=karadmin");
checkFail("GET", "users/check_login?login=jhkjhkjh", 404);
checkWork("GET", "users/check_email?email=admin@admin.ZZZ");
checkFail("GET", "users/check_email?email=ksjhdkjfhskjdh", 404);
// not testable : get_token
}
@Order(5)
@Test
public void testMeWithToken() throws Exception {
loginAdmin();
final String result = api.get(String.class, "users/me");
Assertions.assertEquals("{\"id\":1,\"login\":\"karadmin\"}", result);
LOGGER.info("Remove the test db");
DBEntry.closeAllForceMode();
ConfigBaseVariable.clearAllValue();
}
}

View File

@ -12,17 +12,23 @@ public class WebLauncherTest extends WebLauncher {
public WebLauncherTest() {
LOGGER.debug("Configure REST system");
// for local test:
ConfigBaseVariable.apiAdress = "http://127.0.0.1:12345/test/api/";
ConfigBaseVariable.dbPort = "3306";
ConfigBaseVariable.apiAdress = "http://127.0.0.1:13212/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";
ConfigBaseVariable.dbHost = "localhost";
}
} 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 = "ZERTYSDGFVHSDFGHJYZSDFGSQxfgsqdfgsqdrf4564654";
// ConfigBaseVariable.dbPassword = "password";
}
}
}

View File

@ -0,0 +1,55 @@
services:
kar_db_service:
image: mysql:latest
restart: always
environment:
- MYSQL_ROOT_PASSWORD=base_db_password
volumes:
- ./data:/var/lib/mysql
mem_limit: 300m
ports:
- 3906:3306
healthcheck:
test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
timeout: 10s
retries: 5
# perform a 1 minute grace to let the DB to perform the initialization
start_period: 1m
start_interval: 1m
kar_mongodb_service:
image: mongo:latest
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: base_db_password
ports:
- 27017:27017
volumes:
- ./dataMongo:/data/db
kar_adminer_service:
image: adminer:latest
restart: always
depends_on:
kar_db_service:
condition: service_healthy
links:
- kar_db_service:db
- kar_mongodb_service:dbm
ports:
- 4079:8080
mem_limit: 50m
mongo_express_service:
image: mongo-express
restart: always
ports:
- 4077:8081
links:
- kar_mongodb_service:db
environment:
ME_CONFIG_MONGODB_ADMINUSERNAME: root
ME_CONFIG_MONGODB_ADMINPASSWORD: base_db_password
ME_CONFIG_MONGODB_URL: mongodb://root:base_db_password@db:27017/
ME_CONFIG_BASICAUTH: false

View File

@ -1,12 +0,0 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# You can see what browsers were selected by your queries by running:
# npx browserslist
> 0.5%
last 2 versions
Firefox ESR
not dead
not IE 9-11 # For IE 9-11 support, remove 'not'.

View File

@ -1,13 +0,0 @@
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false

2
front/.env.production Normal file
View File

@ -0,0 +1,2 @@
# URL for database connection
VITE_API_BASE_URL=karusic/api/

View File

@ -1,4 +0,0 @@
node_modules/*
build/*
out/*
dist/*

View File

@ -1,225 +0,0 @@
var OFF = 0, WARN = 1, ERROR = 2;
module.exports = {
'env': {
'browser': true,
'es2021': true,
},
'extends': [
'eslint:recommended',
],
'parser': '@typescript-eslint/parser',
'parserOptions': {
'ecmaVersion': 'latest',
'sourceType': 'module',
},
'plugins': [
'@typescript-eslint',
],
"rules": {
// Possible Errors (overrides from recommended set)
"no-extra-parens": ERROR,
"no-unexpected-multiline": ERROR,
// All JSDoc comments must be valid
"valid-jsdoc": [ OFF, {
"requireReturn": false,
"requireReturnDescription": false,
"requireParamDescription": true,
"prefer": {
"return": "returns"
}
}],
// Best Practices
// Allowed a getter without setter, but all setters require getters
"accessor-pairs": [ OFF, {
"getWithoutSet": false,
"setWithoutGet": true
}],
"block-scoped-var": WARN,
"consistent-return": OFF,
"curly": ERROR,
"default-case": WARN,
// the dot goes with the property when doing multiline
"dot-location": [ WARN, "property" ],
"dot-notation": WARN,
"eqeqeq": [ ERROR, "smart" ],
"guard-for-in": WARN,
"no-alert": ERROR,
"no-caller": ERROR,
"no-case-declarations": WARN,
"no-div-regex": WARN,
"no-else-return": WARN,
"no-empty-pattern": WARN,
"no-eq-null": ERROR,
"no-eval": ERROR,
"no-extend-native": ERROR,
"no-extra-bind": WARN,
"no-floating-decimal": WARN,
"no-implicit-coercion": [ WARN, {
"boolean": true,
"number": true,
"string": true
}],
"no-implied-eval": ERROR,
"no-invalid-this": ERROR,
"no-iterator": ERROR,
"no-labels": WARN,
"no-lone-blocks": WARN,
"no-loop-func": ERROR,
"no-magic-numbers": OFF,
"no-multi-spaces": ERROR,
"no-multi-str": WARN,
"no-native-reassign": ERROR,
"no-new-func": ERROR,
"no-new-wrappers": ERROR,
"no-new": ERROR,
"no-octal-escape": ERROR,
"no-param-reassign": ERROR,
"no-process-env": WARN,
"no-proto": ERROR,
"no-redeclare": ERROR,
"no-return-assign": ERROR,
"no-script-url": ERROR,
"no-self-compare": ERROR,
"no-throw-literal": ERROR,
"no-unused-expressions": ERROR,
"no-useless-call": ERROR,
"no-useless-concat": ERROR,
"no-void": WARN,
// Produce warnings when something is commented as TODO or FIXME
"no-warning-comments": [ WARN, {
"terms": [ "TODO", "FIXME" ],
"location": "start"
}],
"no-with": WARN,
"radix": WARN,
"vars-on-top": ERROR,
// Enforces the style of wrapped functions
"wrap-iife": [ ERROR, "outside" ],
"yoda": ERROR,
// Strict Mode - for ES6, never use strict.
"strict": [ ERROR, "never" ],
// Variables
"init-declarations": [ OFF, "always" ],
"no-catch-shadow": WARN,
"no-delete-var": ERROR,
"no-label-var": ERROR,
"no-shadow-restricted-names": ERROR,
"no-shadow": WARN,
// We require all vars to be initialized (see init-declarations)
// If we NEED a var to be initialized to undefined, it needs to be explicit
"no-undef-init": OFF,
"no-undef": ERROR,
"no-undefined": OFF,
"no-unused-vars": OFF,
// Disallow hoisting - let & const don't allow hoisting anyhow
"no-use-before-define": ERROR,
// Node.js and CommonJS
"callback-return": [ WARN, [ "callback", "next" ]],
"global-require": ERROR,
"handle-callback-err": WARN,
"no-mixed-requires": WARN,
"no-new-require": ERROR,
// Use path.concat instead
"no-path-concat": ERROR,
"no-process-exit": ERROR,
"no-restricted-modules": OFF,
"no-sync": WARN,
// ECMAScript 6 support
"arrow-body-style": [ ERROR, "always" ],
"arrow-parens": [ ERROR, "always" ],
"arrow-spacing": [ ERROR, { "before": true, "after": true }],
"constructor-super": ERROR,
"generator-star-spacing": [ ERROR, "before" ],
"no-confusing-arrow": ERROR,
"no-class-assign": ERROR,
"no-const-assign": ERROR,
"no-dupe-class-members": ERROR,
"no-this-before-super": ERROR,
"no-var": WARN,
"object-shorthand": [ WARN, "never" ],
"prefer-arrow-callback": WARN,
"prefer-spread": WARN,
"prefer-template": WARN,
"require-yield": ERROR,
// Stylistic - everything here is a warning because of style.
"array-bracket-spacing": [ WARN, "always" ],
"block-spacing": [ WARN, "always" ],
"brace-style": [ WARN, "1tbs", { "allowSingleLine": false } ],
"camelcase": WARN,
"comma-spacing": [ WARN, { "before": false, "after": true } ],
"comma-style": [ WARN, "last" ],
"computed-property-spacing": [ WARN, "never" ],
"consistent-this": [ WARN, "self" ],
"eol-last": WARN,
"func-names": WARN,
"func-style": [ WARN, "declaration" ],
"id-length": [ WARN, { "min": 2, "max": 32 } ],
"indent": [ WARN, 'tab' ],
"jsx-quotes": [ WARN, "prefer-double" ],
"linebreak-style": [ WARN, "unix" ],
"lines-around-comment": [ OFF, { "beforeBlockComment": true } ],
"max-depth": [ WARN, 8 ],
"max-len": [ WARN, 182 ],
"max-nested-callbacks": [ WARN, 8 ],
"max-params": [ WARN, 10 ],
"new-cap": OFF,
"new-parens": WARN,
"no-array-constructor": WARN,
"no-bitwise": OFF,
"no-continue": OFF,
"no-inline-comments": OFF,
"no-lonely-if": OFF,
"no-mixed-spaces-and-tabs": OFF,
"no-multiple-empty-lines": WARN,
"no-negated-condition": OFF,
"no-nested-ternary": WARN,
"no-new-object": WARN,
"no-plusplus": OFF,
"no-spaced-func": WARN,
"no-ternary": OFF,
"no-trailing-spaces": WARN,
"no-underscore-dangle": WARN,
"no-unneeded-ternary": WARN,
"object-curly-spacing": [ WARN, "always" ],
"one-var": OFF,
"operator-assignment": [ WARN, "never" ],
"operator-linebreak": [ WARN, "after" ],
"padded-blocks": [ WARN, "never" ],
"quote-props": [ WARN, "consistent-as-needed" ],
"quotes": [ WARN, "single" ],
"require-jsdoc": [ OFF, {
"require": {
"FunctionDeclaration": true,
"MethodDefinition": true,
"ClassDeclaration": false
}
}],
"semi-spacing": [ WARN, { "before": false, "after": true }],
"semi": [ ERROR, "always" ],
"sort-vars": OFF,
"keyword-spacing": [WARN, {
"overrides": {
"if": { "after": false },
"for": { "after": false },
"while": { "after": false },
"static": { "after": false },
"as": { "after": false }
}
}],
"space-before-blocks": [ WARN, "always" ],
"space-before-function-paren": [ WARN, "never" ],
"space-in-parens": [ WARN, "never" ],
"space-infix-ops": [ WARN, { "int32Hint": true } ],
"space-unary-ops": ERROR,
"spaced-comment": [ WARN, "always" ],
"wrap-regex": WARN,
},
};

3
front/.gitignore vendored
View File

@ -1,3 +0,0 @@
/node_modules/
/.angular/
/.idea/

27
front/.storybook/main.ts Normal file
View 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;

View 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>

View File

@ -0,0 +1,43 @@
import React from 'react';
import { Box } from '@chakra-ui/react';
import { ChakraProvider } from '@chakra-ui/react';
import { MemoryRouter } from 'react-router-dom';
import theme from '../src/theme';
// .storybook/preview.js
export const parameters = {
options: {
storySort: {
order: ['StyleGuide', 'Components', 'Fields', 'App Layout'],
},
},
actions: {},
layout: 'fullscreen',
backgrounds: { disable: true, grid: { disable: true } },
chakra: {
theme,
},
};
const DocumentationWrapper = ({ children }) => {
return (
<Box id="start-ui-storybook-wrapper" p="4" pb="8" flex="1">
{children}
</Box>
);
};
export const decorators = [
(Story, context) => (
<ChakraProvider theme={theme}>
{/* Using MemoryRouter to avoid route clashing with Storybook */}
<MemoryRouter>
<DocumentationWrapper>
<Story {...context} />
</DocumentationWrapper>
</MemoryRouter>
</ChakraProvider>
),
];

View File

@ -1,33 +0,0 @@
# base image
FROM node:lts as build
# add `/application/node_modules/.bin` to $PATH
ENV PATH /application/node_modules/.bin:$PATH
ADD package-lock.json /application/
ADD package.json /application/
#ADD browserslist /application/
ADD karma.conf.js /application/
ADD protractor.conf.js /application/
WORKDIR /application/
# install and cache app dependencies
RUN npm install
ADD e2e /application/e2e
ADD tsconfig.json tslint.json angular.json ./
ADD src /application/src
# generate build
RUN ng build --output-path=dist --configuration=production --base-href=/karideo/ --deploy-url=/karideo/
############
### prod ###
############
# base image
FROM httpd:latest
# copy artifact build from the 'build environment'
COPY --from=build /application/dist /usr/local/apache2/htdocs/
COPY httpd/httpd.conf /usr/local/apache2/conf/httpd.conf

View File

@ -1,24 +0,0 @@
# base image
FROM node:latest
ADD src /application/src
ADD e2e /application/e2e
ADD package-lock.json /application/
ADD package.json /application/
ADD angular.json /application/
ADD browserslist /application/
ADD karma.conf.js /application/
ADD protractor.conf.js /application/
ADD tsconfig.json /application/
ADD tslint.json /application/
WORKDIR /application/
# add `/app/node_modules/.bin` to $PATH
ENV PATH /app/node_modules/.bin:$PATH
# install and cache app dependencies
RUN npm install
# start app
CMD ["npx", "ng", "serve", "--host", "0.0.0.0"]

2
front/LICENSE Normal file
View File

@ -0,0 +1,2 @@
Proprietary
@copyright Edouard Dupin 2024

View File

@ -1,172 +0,0 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"karusic": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist",
"index": "src/index.html",
"main": "src/main.ts",
"tsConfig": "src/tsconfig.app.json",
"preserveSymlinks": true,
"polyfills": [
"zone.js"
],
"assets": [
"src/assets",
"src/favicon.ico"
],
"styles": [
"src/styles.less",
"src/generic_page.less",
"src/theme.color.blue.less",
"src/theme.checkbox.less",
"src/theme.modal.less"
],
"scripts": []
},
"configurations": {
"production": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
]
},
"develop": {
"optimization": false,
"outputHashing": "none",
"namedChunks": true,
"aot": false,
"extractLicenses": true,
"vendorChunk": true,
"buildOptimizer": false,
"sourceMap": {
"scripts": true,
"styles": true,
"hidden": false,
"vendor": true
}
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"buildTarget": "karusic:build"
},
"configurations": {
"production": {
"buildTarget": "karusic:build:production"
},
"develop": {
"buildTarget": "karusic:build:develop"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "karusic:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"karmaConfig": "./karma.conf.js",
"polyfills": [
"zone.js"
],
"tsConfig": "src/tsconfig.spec.json",
"scripts": [],
"styles": [
"src/styles.less",
"src/generic_page.less",
"src/theme.color.blue.less",
"src/theme.checkbox.less",
"src/theme.modal.less"
],
"assets": [
"src/assets",
"src/favicon.ico"
]
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"fix": true,
"eslintConfig": ".eslintrc.js",
"lintFilePatterns": [
"src/**/*.spec.ts",
"src/**/*.ts"
]
}
},
"TTTTTTlint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"src/tsconfig.app.json",
"src/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
},
"karusic-e2e": {
"root": "e2e",
"sourceRoot": "e2e",
"projectType": "application",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "./protractor.conf.js",
"devServerTarget": "karusic:serve"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"e2e/tsconfig.e2e.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
"schematics": {
"@schematics/angular:component": {
"prefix": "app",
"style": "less"
},
"@schematics/angular:directive": {
"prefix": "app"
}
}
}

6
front/app-build.json Normal file
View File

@ -0,0 +1,6 @@
{
"display": "__DEVELOPMENT__",
"version": "__VERSION__",
"commit": "__COMMIT__",
"date": "__DATE__"
}

25
front/build.js Normal file
View File

@ -0,0 +1,25 @@
const dayjs = require('dayjs');
const fs = require('fs');
const generateAppBuild = () => {
const getVersion = () => fs.readFileSync('version.txt', 'utf8');
const commit = process.env.VERCEL_GIT_COMMIT_SHA
? process.env.VERCEL_GIT_COMMIT_SHA
: getVersion();
const appBuildContent = {
display: `${dayjs().format('YYYY-MM-DD')}`,
version: `${commit} - ${dayjs().format()}`,
commit,
date: dayjs().format(),
};
fs.writeFileSync(
'./app-build.json',
JSON.stringify(appBuildContent, null, 2)
);
};
generateAppBuild();

View File

@ -1,11 +0,0 @@
version: '3'
services:
karideo_service:
build: .
restart: always
image: yui.heero/karideo
container_name: karideo
ports:
#- 15081:4200
- 15081:80

View File

@ -1,14 +0,0 @@
import { AppPage } from './app.po';
describe('karusic App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getParagraphText()).toEqual('Welcome to app!');
});
});

View File

@ -1,11 +0,0 @@
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get('/');
}
getParagraphText() {
return element(by.css('app-root h1')).getText();
}
}

View File

@ -1,14 +0,0 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"baseUrl": "./",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}

View File

@ -1,541 +0,0 @@
#
# This is the main Apache HTTP server configuration file. It contains the
# configuration directives that give the server its instructions.
# See <URL:http://httpd.apache.org/docs/2.4/> for detailed information.
# In particular, see
# <URL:http://httpd.apache.org/docs/2.4/mod/directives.html>
# for a discussion of each configuration directive.
#
# Do NOT simply read the instructions in here without understanding
# what they do. They're here only as hints or reminders. If you are unsure
# consult the online docs. You have been warned.
#
# Configuration and logfile names: If the filenames you specify for many
# of the server's control files begin with "/" (or "drive:/" for Win32), the
# server will use that explicit path. If the filenames do *not* begin
# with "/", the value of ServerRoot is prepended -- so "logs/access_log"
# with ServerRoot set to "/usr/local/apache2" will be interpreted by the
# server as "/usr/local/apache2/logs/access_log", whereas "/logs/access_log"
# will be interpreted as '/logs/access_log'.
#
# ServerRoot: The top of the directory tree under which the server's
# configuration, error, and log files are kept.
#
# Do not add a slash at the end of the directory path. If you point
# ServerRoot at a non-local disk, be sure to specify a local disk on the
# Mutex directive, if file-based mutexes are used. If you wish to share the
# same ServerRoot for multiple httpd daemons, you will need to change at
# least PidFile.
#
ServerRoot "/usr/local/apache2"
#
# Mutex: Allows you to set the mutex mechanism and mutex file directory
# for individual mutexes, or change the global defaults
#
# Uncomment and change the directory if mutexes are file-based and the default
# mutex file directory is not on a local disk or is not appropriate for some
# other reason.
#
# Mutex default:logs
#
# Listen: Allows you to bind Apache to specific IP addresses and/or
# ports, instead of the default. See also the <VirtualHost>
# directive.
#
# Change this to Listen on specific IP addresses as shown below to
# prevent Apache from glomming onto all bound IP addresses.
#
Listen 80
Listen 443
#
# Dynamic Shared Object (DSO) Support
#
# To be able to use the functionality of a module which was built as a DSO you
# have to place corresponding `LoadModule' lines at this location so the
# directives contained in it are actually available _before_ they are used.
# Statically compiled modules (those listed by `httpd -l') do not need
# to be loaded here.
#
# Example:
# LoadModule foo_module modules/mod_foo.so
#
LoadModule mpm_event_module modules/mod_mpm_event.so
#LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
#LoadModule mpm_worker_module modules/mod_mpm_worker.so
LoadModule authn_file_module modules/mod_authn_file.so
#LoadModule authn_dbm_module modules/mod_authn_dbm.so
#LoadModule authn_anon_module modules/mod_authn_anon.so
#LoadModule authn_dbd_module modules/mod_authn_dbd.so
#LoadModule authn_socache_module modules/mod_authn_socache.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authz_host_module modules/mod_authz_host.so
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
LoadModule authz_user_module modules/mod_authz_user.so
#LoadModule authz_dbm_module modules/mod_authz_dbm.so
#LoadModule authz_owner_module modules/mod_authz_owner.so
#LoadModule authz_dbd_module modules/mod_authz_dbd.so
LoadModule authz_core_module modules/mod_authz_core.so
#LoadModule authnz_ldap_module modules/mod_authnz_ldap.so
#LoadModule authnz_fcgi_module modules/mod_authnz_fcgi.so
LoadModule access_compat_module modules/mod_access_compat.so
LoadModule auth_basic_module modules/mod_auth_basic.so
#LoadModule auth_form_module modules/mod_auth_form.so
#LoadModule auth_digest_module modules/mod_auth_digest.so
#LoadModule allowmethods_module modules/mod_allowmethods.so
#LoadModule isapi_module modules/mod_isapi.so
#LoadModule file_cache_module modules/mod_file_cache.so
#LoadModule cache_module modules/mod_cache.so
#LoadModule cache_disk_module modules/mod_cache_disk.so
#LoadModule cache_socache_module modules/mod_cache_socache.so
#LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
#LoadModule socache_dbm_module modules/mod_socache_dbm.so
#LoadModule socache_memcache_module modules/mod_socache_memcache.so
#LoadModule watchdog_module modules/mod_watchdog.so
#LoadModule macro_module modules/mod_macro.so
#LoadModule dbd_module modules/mod_dbd.so
#LoadModule bucketeer_module modules/mod_bucketeer.so
#LoadModule dumpio_module modules/mod_dumpio.so
#LoadModule echo_module modules/mod_echo.so
#LoadModule example_hooks_module modules/mod_example_hooks.so
#LoadModule case_filter_module modules/mod_case_filter.so
#LoadModule case_filter_in_module modules/mod_case_filter_in.so
#LoadModule example_ipc_module modules/mod_example_ipc.so
#LoadModule buffer_module modules/mod_buffer.so
#LoadModule data_module modules/mod_data.so
#LoadModule ratelimit_module modules/mod_ratelimit.so
LoadModule reqtimeout_module modules/mod_reqtimeout.so
#LoadModule ext_filter_module modules/mod_ext_filter.so
#LoadModule request_module modules/mod_request.so
#LoadModule include_module modules/mod_include.so
LoadModule filter_module modules/mod_filter.so
#LoadModule reflector_module modules/mod_reflector.so
#LoadModule substitute_module modules/mod_substitute.so
#LoadModule sed_module modules/mod_sed.so
#LoadModule charset_lite_module modules/mod_charset_lite.so
#LoadModule deflate_module modules/mod_deflate.so
LoadModule xml2enc_module modules/mod_xml2enc.so
LoadModule proxy_html_module modules/mod_proxy_html.so
LoadModule mime_module modules/mod_mime.so
#LoadModule ldap_module modules/mod_ldap.so
LoadModule log_config_module modules/mod_log_config.so
#LoadModule log_debug_module modules/mod_log_debug.so
#LoadModule log_forensic_module modules/mod_log_forensic.so
#LoadModule logio_module modules/mod_logio.so
#LoadModule lua_module modules/mod_lua.so
LoadModule env_module modules/mod_env.so
#LoadModule mime_magic_module modules/mod_mime_magic.so
#LoadModule cern_meta_module modules/mod_cern_meta.so
#LoadModule expires_module modules/mod_expires.so
LoadModule headers_module modules/mod_headers.so
#LoadModule ident_module modules/mod_ident.so
#LoadModule usertrack_module modules/mod_usertrack.so
#LoadModule unique_id_module modules/mod_unique_id.so
LoadModule setenvif_module modules/mod_setenvif.so
LoadModule version_module modules/mod_version.so
#LoadModule remoteip_module modules/mod_remoteip.so
LoadModule proxy_module modules/mod_proxy.so
#LoadModule proxy_connect_module modules/mod_proxy_connect.so
#LoadModule proxy_ftp_module modules/mod_proxy_ftp.so
LoadModule proxy_http_module modules/mod_proxy_http.so
#LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
#LoadModule proxy_scgi_module modules/mod_proxy_scgi.so
#LoadModule proxy_uwsgi_module modules/mod_proxy_uwsgi.so
#LoadModule proxy_fdpass_module modules/mod_proxy_fdpass.so
#LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
#LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
#LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
#LoadModule proxy_express_module modules/mod_proxy_express.so
#LoadModule proxy_hcheck_module modules/mod_proxy_hcheck.so
#LoadModule session_module modules/mod_session.so
#LoadModule session_cookie_module modules/mod_session_cookie.so
#LoadModule session_crypto_module modules/mod_session_crypto.so
#LoadModule session_dbd_module modules/mod_session_dbd.so
#LoadModule slotmem_shm_module modules/mod_slotmem_shm.so
#LoadModule slotmem_plain_module modules/mod_slotmem_plain.so
LoadModule ssl_module modules/mod_ssl.so
#LoadModule optional_hook_export_module modules/mod_optional_hook_export.so
#LoadModule optional_hook_import_module modules/mod_optional_hook_import.so
#LoadModule optional_fn_import_module modules/mod_optional_fn_import.so
#LoadModule optional_fn_export_module modules/mod_optional_fn_export.so
#LoadModule dialup_module modules/mod_dialup.so
#LoadModule http2_module modules/mod_http2.so
#LoadModule proxy_http2_module modules/mod_proxy_http2.so
#LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so
#LoadModule lbmethod_bytraffic_module modules/mod_lbmethod_bytraffic.so
#LoadModule lbmethod_bybusyness_module modules/mod_lbmethod_bybusyness.so
#LoadModule lbmethod_heartbeat_module modules/mod_lbmethod_heartbeat.so
LoadModule unixd_module modules/mod_unixd.so
#LoadModule heartbeat_module modules/mod_heartbeat.so
#LoadModule heartmonitor_module modules/mod_heartmonitor.so
#LoadModule dav_module modules/mod_dav.so
LoadModule status_module modules/mod_status.so
LoadModule autoindex_module modules/mod_autoindex.so
#LoadModule asis_module modules/mod_asis.so
#LoadModule info_module modules/mod_info.so
#LoadModule suexec_module modules/mod_suexec.so
<IfModule !mpm_prefork_module>
#LoadModule cgid_module modules/mod_cgid.so
</IfModule>
<IfModule mpm_prefork_module>
#LoadModule cgi_module modules/mod_cgi.so
</IfModule>
#LoadModule dav_fs_module modules/mod_dav_fs.so
#LoadModule dav_lock_module modules/mod_dav_lock.so
#LoadModule vhost_alias_module modules/mod_vhost_alias.so
#LoadModule negotiation_module modules/mod_negotiation.so
LoadModule dir_module modules/mod_dir.so
#LoadModule imagemap_module modules/mod_imagemap.so
#LoadModule actions_module modules/mod_actions.so
#LoadModule speling_module modules/mod_speling.so
#LoadModule userdir_module modules/mod_userdir.so
LoadModule alias_module modules/mod_alias.so
LoadModule rewrite_module modules/mod_rewrite.so
<IfModule unixd_module>
#
# If you wish httpd to run as a different user or group, you must run
# httpd as root initially and it will switch.
#
# User/Group: The name (or #number) of the user/group to run httpd as.
# It is usually good practice to create a dedicated user and group for
# running httpd, as with most system services.
#
User daemon
Group daemon
</IfModule>
# 'Main' server configuration
#
# The directives in this section set up the values used by the 'main'
# server, which responds to any requests that aren't handled by a
# <VirtualHost> definition. These values also provide defaults for
# any <VirtualHost> containers you may define later in the file.
#
# All of these directives may appear inside <VirtualHost> containers,
# in which case these default settings will be overridden for the
# virtual host being defined.
#
#
# ServerAdmin: Your address, where problems with the server should be
# e-mailed. This address appears on some server-generated pages, such
# as error documents. e.g. admin@your-domain.com
#
ServerAdmin yui.heero@gmail.com
#
# ServerName gives the name and port that the server uses to identify itself.
# This can often be determined automatically, but we recommend you specify
# it explicitly to prevent problems during startup.
#
# If your host doesn't have a registered DNS name, enter its IP address here.
#
#ServerName www.example.com:80
#
# Deny access to the entirety of your server's filesystem. You must
# explicitly permit access to web content directories in other
# <Directory> blocks below.
#
<Directory />
AllowOverride none
Require all denied
</Directory>
# intermediate configuration, tweak to your needs
SSLProtocol all -SSLv2 -SSLv3
SSLCipherSuite ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS
SSLHonorCipherOrder on
#
# Note that from this point forward you must specifically allow
# particular features to be enabled - so if something's not working as
# you might expect, make sure that you have specifically enabled it
# below.
#
#
# DocumentRoot: The directory out of which you will serve your
# documents. By default, all requests are taken from this directory, but
# symbolic links and aliases may be used to point to other locations.
#
<VirtualHost *:80>
ServerName my-app
DocumentRoot "/usr/local/apache2/htdocs"
<Directory "/usr/local/apache2/htdocs">
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
RewriteEngine on
# Don't rewrite files or directories
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# Rewrite everything else to index.html to allow HTML5 state links
RewriteRule ^ index.html [L]
</Directory>
</VirtualHost>
#
# DirectoryIndex: sets the file that Apache will serve if a directory
# is requested.
#
<IfModule dir_module>
DirectoryIndex index.html
</IfModule>
#
# The following lines prevent .htaccess and .htpasswd files from being
# viewed by Web clients.
#
<Files ".ht*">
Require all denied
</Files>
#
# ErrorLog: The location of the error log file.
# If you do not specify an ErrorLog directive within a <VirtualHost>
# container, error messages relating to that virtual host will be
# logged here. If you *do* define an error logfile for a <VirtualHost>
# container, that host's errors will be logged there and not here.
#
ErrorLog /proc/self/fd/2
#
# LogLevel: Control the number of messages logged to the error_log.
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
#
LogLevel warn
<IfModule log_config_module>
#
# The following directives define some format nicknames for use with
# a CustomLog directive (see below).
#
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b" common
<IfModule logio_module>
# You need to enable mod_logio.c to use %I and %O
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
</IfModule>
#
# The location and format of the access logfile (Common Logfile Format).
# If you do not define any access logfiles within a <VirtualHost>
# container, they will be logged here. Contrariwise, if you *do*
# define per-<VirtualHost> access logfiles, transactions will be
# logged therein and *not* in this file.
#
CustomLog /proc/self/fd/1 common
#
# If you prefer a logfile with access, agent, and referer information
# (Combined Logfile Format) you can use the following directive.
#
#CustomLog "logs/access_log" combined
</IfModule>
<IfModule alias_module>
#
# Redirect: Allows you to tell clients about documents that used to
# exist in your server's namespace, but do not anymore. The client
# will make a new request for the document at its new location.
# Example:
# Redirect permanent /foo http://www.example.com/bar
#
# Alias: Maps web paths into filesystem paths and is used to
# access content that does not live under the DocumentRoot.
# Example:
# Alias /webpath /full/filesystem/path
#
# If you include a trailing / on /webpath then the server will
# require it to be present in the URL. You will also likely
# need to provide a <Directory> section to allow access to
# the filesystem path.
#
# ScriptAlias: This controls which directories contain server scripts.
# ScriptAliases are essentially the same as Aliases, except that
# documents in the target directory are treated as applications and
# run by the server when requested rather than as documents sent to the
# client. The same rules about trailing "/" apply to ScriptAlias
# directives as to Alias.
#
ScriptAlias /cgi-bin/ "/usr/local/apache2/cgi-bin/"
</IfModule>
<IfModule cgid_module>
#
# ScriptSock: On threaded servers, designate the path to the UNIX
# socket used to communicate with the CGI daemon of mod_cgid.
#
#Scriptsock cgisock
</IfModule>
#
# "/usr/local/apache2/cgi-bin" should be changed to whatever your ScriptAliased
# CGI directory exists, if you have that configured.
#
<Directory "/usr/local/apache2/cgi-bin">
AllowOverride None
Options None
Require all granted
</Directory>
<IfModule headers_module>
#
# Avoid passing HTTP_PROXY environment to CGI's on this or any proxied
# backend servers which have lingering "httpoxy" defects.
# 'Proxy' request header is undefined by the IETF, not listed by IANA
#
RequestHeader unset Proxy early
</IfModule>
<IfModule mime_module>
#
# TypesConfig points to the file containing the list of mappings from
# filename extension to MIME-type.
#
TypesConfig conf/mime.types
#
# AddType allows you to add to or override the MIME configuration
# file specified in TypesConfig for specific file types.
#
#AddType application/x-gzip .tgz
#
# AddEncoding allows you to have certain browsers uncompress
# information on the fly. Note: Not all browsers support this.
#
#AddEncoding x-compress .Z
#AddEncoding x-gzip .gz .tgz
#
# If the AddEncoding directives above are commented-out, then you
# probably should define those extensions to indicate media types:
#
AddType application/x-compress .Z
AddType application/x-gzip .gz .tgz
#
# AddHandler allows you to map certain file extensions to "handlers":
# actions unrelated to filetype. These can be either built into the server
# or added with the Action directive (see below)
#
# To use CGI scripts outside of ScriptAliased directories:
# (You will also need to add "ExecCGI" to the "Options" directive.)
#
#AddHandler cgi-script .cgi
# For type maps (negotiated resources):
#AddHandler type-map var
#
# Filters allow you to process content before it is sent to the client.
#
# To parse .shtml files for server-side includes (SSI):
# (You will also need to add "Includes" to the "Options" directive.)
#
#AddType text/html .shtml
#AddOutputFilter INCLUDES .shtml
</IfModule>
#
# The mod_mime_magic module allows the server to use various hints from the
# contents of the file itself to determine its type. The MIMEMagicFile
# directive tells the module where the hint definitions are located.
#
#MIMEMagicFile conf/magic
#
# Customizable error responses come in three flavors:
# 1) plain text 2) local redirects 3) external redirects
#
# Some examples:
#ErrorDocument 500 "The server made a boo boo."
#ErrorDocument 404 /missing.html
#ErrorDocument 404 "/cgi-bin/missing_handler.pl"
#ErrorDocument 402 http://www.example.com/subscription_info.html
#
#
# MaxRanges: Maximum number of Ranges in a request before
# returning the entire resource, or one of the special
# values 'default', 'none' or 'unlimited'.
# Default setting is to accept 200 Ranges.
#MaxRanges unlimited
#
# EnableMMAP and EnableSendfile: On systems that support it,
# memory-mapping or the sendfile syscall may be used to deliver
# files. This usually improves server performance, but must
# be turned off when serving from networked-mounted
# filesystems or if support for these functions is otherwise
# broken on your system.
# Defaults: EnableMMAP On, EnableSendfile Off
#
#EnableMMAP off
#EnableSendfile on
# Supplemental configuration
#
# The configuration files in the conf/extra/ directory can be
# included to add extra features or to modify the default configuration of
# the server, or you may simply copy their contents here and change as
# necessary.
# Server-pool management (MPM specific)
#Include conf/extra/httpd-mpm.conf
# Multi-language error messages
#Include conf/extra/httpd-multilang-errordoc.conf
# Fancy directory listings
#Include conf/extra/httpd-autoindex.conf
# Language settings
#Include conf/extra/httpd-languages.conf
# User home directories
#Include conf/extra/httpd-userdir.conf
# Real-time info on requests and configuration
#Include conf/extra/httpd-info.conf
# Virtual hosts
#Include conf/extra/httpd-vhosts.conf
# Local access to the Apache HTTP Server Manual
#Include conf/extra/httpd-manual.conf
# Distributed authoring and versioning (WebDAV)
#Include conf/extra/httpd-dav.conf
# Various default settings
#Include conf/extra/httpd-default.conf
# Configure mod_proxy_html to understand HTML4/XHTML1
<IfModule proxy_html_module>
Include conf/extra/proxy-html.conf
</IfModule>
# Secure (SSL/TLS) connections
#Include conf/extra/httpd-ssl.conf
#
# Note: The following must must be present to support
# starting without SSL on platforms with no /dev/random equivalent
# but a statically compiled-in mod_ssl.
#
<IfModule ssl_module>
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin
</IfModule>

13
front/index.html Normal file
View 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>Karusic</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>

View File

@ -1,31 +0,0 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client:{
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};

18
front/knip.ts Normal file
View File

@ -0,0 +1,18 @@
import type { KnipConfig } from 'knip';
const config: KnipConfig = {
// Ignoring mostly shell binaries
ignoreBinaries: ['export', 'sleep'],
ignore: [
// Related to tests
'tests/**',
'**.conf.js',
'steps.d.ts',
'steps_file.js',
'env_ci/codecept.conf.js',
// Generic components are useful.
'src/components/**',
],
};
export default config;

View File

@ -1,49 +1,111 @@
{
"name": "karusic",
"version": "0.0.0",
"license": "MPL-2",
"scripts": {
"all": "npm run build && pnpm run test",
"ng": "ng",
"dev": "ng serve --configuration=develop --watch --port 4203",
"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,
"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 --upgrade",
"install_dependency": "pnpm install",
"test": "vitest run",
"test:watch": "vitest watch",
"build": "tsc && vite build",
"static:build": "node build.js && 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": {
"@angular/animations": "^17.3.4",
"@angular/cdk": "^17.3.4",
"@angular/common": "^17.3.4",
"@angular/compiler": "^17.3.4",
"@angular/core": "^17.3.4",
"@angular/forms": "^17.3.4",
"@angular/material": "^17.3.4",
"@angular/platform-browser": "^17.3.4",
"@angular/platform-browser-dynamic": "^17.3.4",
"@angular/router": "^17.3.4",
"rxjs": "^7.8.1",
"zone.js": "^0.14.4",
"zod": "3.22.4",
"@kangaroo-and-rabbit/kar-cw": "^0.2.0"
"@chakra-ui/anatomy": "2.2.2",
"@chakra-ui/cli": "2.4.1",
"@chakra-ui/react": "2.8.2",
"@chakra-ui/theme-tools": "2.1.2",
"@dnd-kit/core": "6.1.0",
"@dnd-kit/modifiers": "7.0.0",
"@dnd-kit/sortable": "8.0.0",
"@dnd-kit/utilities": "3.2.2",
"@emotion/react": "11.13.3",
"@emotion/styled": "11.13.0",
"@formiz/core": "2.3.1",
"@formiz/validations": "2.0.1",
"allotment": "1.20.2",
"css-mediaquery": "0.1.2",
"dayjs": "1.11.13",
"history": "5.3.0",
"react": "18.3.1",
"react-color-palette": "7.3.0",
"react-currency-input-field": "3.8.0",
"react-custom-scrollbars": "4.2.1",
"react-day-picker": "9.0.9",
"react-dom": "18.3.1",
"react-error-boundary": "4.0.13",
"react-focus-lock": "2.13.2",
"react-icons": "5.3.0",
"react-popper": "2.3.0",
"react-router-dom": "6.26.2",
"react-select": "5.8.0",
"react-simple-keyboard": "3.8.1",
"react-sticky-el": "2.1.1",
"react-use": "17.5.1",
"react-use-draggable-scroll": "0.4.7",
"react-virtuoso": "4.10.4",
"ts-pattern": "5.3.1",
"uuid": "10.0.0",
"zod": "3.23.8",
"zustand": "4.5.5"
},
"devDependencies": {
"@angular-devkit/build-angular": "^17.3.4",
"@angular-eslint/builder": "17.3.0",
"@angular-eslint/eslint-plugin": "17.3.0",
"@angular-eslint/eslint-plugin-template": "17.3.0",
"@angular-eslint/schematics": "17.3.0",
"@angular-eslint/template-parser": "17.3.0",
"@angular/cli": "^17.3.4",
"@angular/compiler-cli": "^17.3.4",
"@angular/language-service": "^17.3.4",
"npm-check-updates": "^16.14.18",
"tslib": "^2.6.2"
"@chakra-ui/styled-system": "2.9.2",
"@playwright/test": "1.47.1",
"@storybook/addon-actions": "8.3.0",
"@storybook/addon-essentials": "8.3.0",
"@storybook/addon-links": "8.3.0",
"@storybook/addon-mdx-gfm": "8.3.0",
"@storybook/react": "8.3.0",
"@storybook/react-vite": "8.3.0",
"@storybook/theming": "8.3.0",
"@testing-library/jest-dom": "6.5.0",
"@testing-library/react": "16.0.1",
"@testing-library/user-event": "14.5.2",
"@trivago/prettier-plugin-sort-imports": "4.3.0",
"@types/jest": "29.5.13",
"@types/node": "22.5.5",
"@types/react": "18.3.5",
"@types/react-dom": "18.3.0",
"@types/react-sticky-el": "1.0.7",
"@typescript-eslint/eslint-plugin": "8.5.0",
"@typescript-eslint/parser": "8.5.0",
"@vitejs/plugin-react": "4.3.1",
"eslint": "9.10.0",
"eslint-plugin-codeceptjs": "1.3.0",
"eslint-plugin-import": "2.30.0",
"eslint-plugin-react": "7.36.1",
"eslint-plugin-react-hooks": "4.6.2",
"eslint-plugin-storybook": "0.8.0",
"jest": "29.7.0",
"jest-environment-jsdom": "29.7.0",
"knip": "5.30.2",
"lint-staged": "15.2.10",
"npm-check-updates": "^17.1.1",
"prettier": "3.3.3",
"puppeteer": "23.3.0",
"react-is": "18.3.1",
"storybook": "8.3.0",
"ts-node": "10.9.2",
"typescript": "5.6.2",
"vite": "5.4.5",
"vitest": "2.1.1"
}
}

File diff suppressed because it is too large Load Diff

16
front/prettier.config.js Normal file
View 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'],
};

View File

@ -1,28 +0,0 @@
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./e2e/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: 'e2e/tsconfig.e2e.json'
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,43 +0,0 @@
Start the application:
```
npm install
```
upgrade package
```
npm audit fix
```
## npm install -g angular-cli
start the application:
```
npx ng serve --watch
```
plus facilement:
npm install @angular-devkit/build-angular@0.901.9
npm start
Apply linter:
==============
```
npx ng lint
```
build the local image:
docker build -t gitea.atria-soft.org/kangaroo-and-rabbit/karusic:latest .
docker login gitea.atria-soft.org
docker push gitea.atria-soft.org/kangaroo-and-rabbit/karusic:latest

118
front/src/App.tsx Normal file
View File

@ -0,0 +1,118 @@
import { useState } from 'react';
import { ChakraProvider, Select } from '@chakra-ui/react';
import {
Box,
Button,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Stack,
Text,
useDisclosure,
} from '@chakra-ui/react';
import { environment } from '@/environment';
import { App as SpaApp } from '@/scene/App';
import { USERS } from '@/service/session';
import theme from '@/theme';
import { hashLocalData } from '@/utils/sso';
const AppEnvHint = () => {
const modal = 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 = () => {
modal.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
zIndex="100000"
position="fixed"
top="0"
insetStart="0"
insetEnd="0"
h="2px"
bg="warning.400"
as="button"
cursor="pointer"
data-test-id="devtools"
onClick={modal.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>
<Modal isOpen={modal.isOpen} onClose={modal.onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Outils développeurs</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Stack>
<Text>Utilisateur</Text>
<Select placeholder="Select test user" onChange={handleChange}>
{Object.keys(USERS).map((key) => (
<option value={key} key={key}>
{key}
</option>
))}
</Select>
</Stack>
</ModalBody>
<ModalFooter>
<Button onClick={onClose}>Apply</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
const App = () => {
return (
<ChakraProvider theme={theme}>
<AppEnvHint />
<SpaApp />
</ChakraProvider>
);
};
export default App;

View File

@ -1,184 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2018, Edouard DUPIN, all right reserved
* @license PROPRIETARY (see license file)
*/
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router'; // CLI imports router
import {
HelpScene, HomeScene, AlbumEditScene, AlbumsScene, ArtistEditScene, ArtistScene, SettingsScene,
GenderScene, PlaylistScene, TrackEditScene, TrackScene, UploadScene, ArtistsScene, ArtistAlbumScene, AlbumScene
} from './scene';
import { ForbiddenScene, OnlyUsersGuardHome, HomeOutScene, OnlyUnregisteredGuardHome, SsoScene, OnlyAdminGuard, OnlyUsersGuard, NotFound404Scene } from '@kangaroo-and-rabbit/kar-cw';
// import { HelpComponent } from './help/help.component';
// see https://angular.io/guide/router
const routes: Routes = [
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: 'forbidden', component: ForbiddenScene },
// ------------------------------------
// -- home global interface
// ------------------------------------
{
path: 'home',
component: HomeScene,
canActivate: [OnlyUsersGuardHome], // this route to unregistered path when not logged ==> permit to simplify display
},
{
path: 'unregistered',
component: HomeOutScene,
canActivate: [OnlyUnregisteredGuardHome], // jump to the home when registered
},
// ------------------------------------
// -- SSO Generic interface
// ------------------------------------
{ path: 'sso/:data/:keepConnected/:token', component: SsoScene },
{ path: 'sso/:keepConnected/:token', component: SsoScene },
{ path: 'sso', component: SsoScene },
// ------------------------------------
// -- Generic pages
// ------------------------------------
{ path: 'help/:page', component: HelpScene },
{ path: 'help', component: HelpScene },
// ------------------------------------
// -- upload new data:
// ------------------------------------
{
path: 'upload',
component: UploadScene,
canActivate: [OnlyAdminGuard],
},
// ------------------------------------
// -- gender:
// ------------------------------------
// display all gender
{
path: 'gender',
component: GenderScene,
canActivate: [OnlyUsersGuard],
},
// display all (artist | album | tracks)
{
path: 'gender/:genderId',
component: GenderScene,
canActivate: [OnlyUsersGuard],
},
//{ path: 'gender-edit/:genderId', component: GenderEditScene },
//{ path: 'gender/:genderId', component: GenderScene },
//{ path: 'gender/:genderId/:artistId/:albumId/:trackId', component: GenderScene },
// ------------------------------------
// -- playlist:
// ------------------------------------
{
path: 'playlist',
component: PlaylistScene,
canActivate: [OnlyUsersGuard],
},
{
path: 'playlist/:playlistId',
component: PlaylistScene,
canActivate: [OnlyUsersGuard],
},
//{ path: 'playlist-edit/:playlistId', component: PlaylistEditScene },
// ------------------------------------
// -- Artist:
// ------------------------------------
// display list of all artist
{
path: 'artist',
component: ArtistsScene,
canActivate: [OnlyUsersGuard],
},
// display list af all artist with a specific gender
{
path: 'artist/:artistId',
component: ArtistScene,
canActivate: [OnlyUsersGuard],
},
{
path: 'artist/:artistId/edit',
component: ArtistEditScene,
canActivate: [OnlyAdminGuard],
},
{
path: 'artist/:artistId/:albumId',
component: ArtistAlbumScene,
canActivate: [OnlyUsersGuard],
},
// ------------------------------------
// -- Album:
// ------------------------------------
// display all Album
{
path: 'album',
component: AlbumsScene,
canActivate: [OnlyUsersGuard],
},
{
path: 'album/:albumId/edit',
component: AlbumEditScene,
canActivate: [OnlyAdminGuard],
},
{
path: 'album/:albumId',
component: AlbumScene,
canActivate: [OnlyUsersGuard],
},
// ------------------------------------
// -- Tracks:
// ------------------------------------
{
path: 'track/:trackId/edit',
component: TrackEditScene,
canActivate: [OnlyAdminGuard],
},
{
path: 'track/:genderId/:artistId/:albumId/:trackId',
component: TrackScene,
canActivate: [OnlyUsersGuard],
},
// ------------------------------------
// -- setting:
// ------------------------------------
{
path: 'settings',
component: SettingsScene,
canActivate: [OnlyUsersGuard],
},
{
path: '**',
component: NotFound404Scene,
},
];
@NgModule({
imports: [
RouterModule.forRoot(
routes,
{
//enableTracing: true, // <-- debugging purposes only
},
),
],
exports: [
RouterModule,
]
})
export class AppRoutingModule { }

View File

@ -1,17 +0,0 @@
<!-- Generic global menu -->
<karcw-top-menu [menu]="currentMenu" (callback)="eventOnMenu($event)"/>
<!-- all interfaced pages -->
<div class="main-content">
@if(autoConnectedDone) {
<router-outlet ></router-outlet>
}
@else {
<div class="generic-page">
<div class="fill-all colomn_mutiple">
<b style="color:red;">Auto-connection in progress</b>
<div class="clear"></div>
</div>
</div>
}
</div>
<app-element-player-audio></app-element-player-audio>

View File

@ -1,46 +0,0 @@
#create-exercice-button {
position: fixed;
display: block;
right: 0;
bottom: 0;
margin-right: 40px;
margin-bottom: 40px;
z-index: 900;
}
#save-exercice-button {
position: fixed;
display: block;
right: 0;
bottom: 0;
margin-right: 110px;
margin-bottom: 40px;
z-index: 900;
}
.main-content {
position: absolute;
//width: ~"calc(calc(100% / 5 ) * 5)";
width: 100%;
height: ~"calc(100% - 56px)";
top: 56px;
left: 0;
margin: 0;
padding: 0;
display: block;
position: fixed;
overflow-y: auto;
//background-color:#FF0;
/*
.main-reduce {
width: 40%;
height: 100%;
margin: 0;
padding: 0px 10% 0px 10%;
display: block;
overflow-y:scroll;
}
*/
}

View File

@ -1,300 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2018, Edouard DUPIN, all right reserved
* @license PROPRIETARY (see license file)
*/
import { Component, OnInit } from '@angular/core';
import { ArianeService } from './service';
import {
MenuItem,
SSOService,
SessionService,
UserService,
UserRoles222,
EventOnMenu,
MenuPosition,
isNullOrUndefined,
} from '@kangaroo-and-rabbit/kar-cw';
enum MenuEventType {
SSO_LOGIN = "SSO_CALL_LOGIN",
SSO_LOGOUT = "SSO_CALL_LOGOUT",
SSO_SIGNUP = "SSO_CALL_SIGNUP",
SEGMENT = "SEGMENT",
TYPE = "TYPE",
ARTIST = "ARTIST",
ALBUM = "ALBUM",
TRACK = "TRACK",
PLAYLIST = "PLAYLIST",
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: [
'./app.component.less',
]
})
export class AppComponent implements OnInit {
title: string = 'Karideo';
autoConnectedDone: boolean = false;
isConnected: boolean = false;
signUpEnable: boolean = true;
currentMenu: MenuItem[] = [];
location: string = "home";
constructor(
private sessionService: SessionService,
private ssoService: SSOService,
private arianeService: ArianeService,
private userService: UserService
) {
}
ngOnInit() {
this.autoConnectedDone = false;
this.isConnected = false;
this.updateMainMenu();
let self = this;
this.sessionService.change.subscribe((isConnected) => {
console.log(`receive event from session ...${isConnected}`);
self.isConnected = isConnected;
self.autoConnectedDone = true;
self.updateMainMenu();
});
this.ssoService.checkSignUpEnable()
.then((value: boolean) => {
console.log(`Get value signUp = ${value}`);
self.signUpEnable = value;
self.updateMainMenu();
}).catch((error: any) => {
console.log(`Can not call the sso to check the sign-up_interface: ${error}`);
});
this.userService.checkAutoConnect().then(() => {
console.log(` ==>>>>> Autoconnect THEN !!!`);
self.autoConnectedDone = true;
}).catch(() => {
console.log(` ==>>>>> Autoconnect CATCH !!!`);
self.autoConnectedDone = true;
}).finally(() => {
console.log(` ==>>>>> Autoconnect FINALLY !!!`);
self.autoConnectedDone = true;
});
this.arianeService.segmentChange.subscribe((_segmentName: string) => {
//console.log(`>>> change typeId=${typeId}`);
self.updateMainMenu();
});
this.arianeService.typeChange.subscribe((_typeId: number) => {
//console.log(`>>> change typeId=${typeId}`);
self.updateMainMenu();
});
this.arianeService.playlistChange.subscribe((_universId: number) => {
//console.log(`>>> change universId=${universId}`);
self.updateMainMenu();
});
this.arianeService.artistChange.subscribe((_artistId: number) => {
//console.log(`>>> change artistId=${artistId}`);
self.updateMainMenu();
});
this.arianeService.albumChange.subscribe((_albumId: number) => {
//console.log(`>>> change albumId=${albumId}`);
self.updateMainMenu();
});
this.arianeService.trackChange.subscribe((_trackId: number) => {
//console.log(`>>> change trackId=${trackId}`);
self.updateMainMenu();
});
}
eventOnMenu(data: EventOnMenu): void {
console.log(`Get event on menu: ${JSON.stringify(data, null, 4)}`);
switch (data.menu.otherData) {
case MenuEventType.SSO_LOGIN:
this.ssoService.requestSignIn();
break;
case MenuEventType.SSO_LOGOUT:
this.ssoService.requestSignOut();
break;
case MenuEventType.SSO_SIGNUP:
this.ssoService.requestSignUp();
break;
case MenuEventType.SEGMENT:
if (this.arianeService.getCurrrentSegment() === "artist") {
this.arianeService.navigateArtist({});
} else if (this.arianeService.getCurrrentSegment() === "gender") {
this.arianeService.navigateGender({});
} else if (this.arianeService.getCurrrentSegment() === "playlist") {
this.arianeService.navigatePlaylist({});
} else if (this.arianeService.getCurrrentSegment() === "track") {
this.arianeService.navigateTrack({});
} else if (this.arianeService.getCurrrentSegment() === "album") {
this.arianeService.navigateAlbum({});
}
break;
case MenuEventType.TYPE:
break;
case MenuEventType.ARTIST:
if (this.arianeService.getCurrrentSegment() === "artist") {
this.arianeService.navigateArtist({ artistId: this.arianeService.getArtistId() });
}
break;
case MenuEventType.ALBUM:
break;
case MenuEventType.TRACK:
break;
case MenuEventType.PLAYLIST:
break;
}
}
updateMainMenu(): void {
console.log("update main menu :");
if (this.isConnected) {
this.currentMenu = [
{
position: MenuPosition.LEFT,
hover: "lkjljlk", //`You are logged as: ${this.sessionService.getLogin()}`,
icon: "menu",
title: "Menu",
subMenu: [
{
position: MenuPosition.LEFT,
hover: "Go to Home page",
icon: "home",
title: "Home",
navigateTo: "home",
}, {
position: MenuPosition.LEFT,
icon: "group_work",
title: this.getSegmentDisplayable(),
otherData: MenuEventType.SEGMENT,
callback: true,
enable: this.getSegmentDisplayable() !== "",
}, {
position: MenuPosition.LEFT,
icon: "piano",
title: this.getSegmentDisplayable(),
otherData: MenuEventType.TYPE,
callback: true,
enable: !isNullOrUndefined(this.arianeService.getTypeId()),
}, {
position: MenuPosition.LEFT,
icon: "person",
title: this.arianeService.getArtistName(),
otherData: MenuEventType.ARTIST,
callback: true,
enable: !isNullOrUndefined(this.arianeService.getArtistId()),
}, {
position: MenuPosition.LEFT,
icon: "album",
title: this.arianeService.getAlbumName(),
otherData: MenuEventType.ALBUM,
callback: true,
enable: !isNullOrUndefined(this.arianeService.getAlbumId()),
}, {
position: MenuPosition.LEFT,
icon: "music_note",
title: this.arianeService.getTrackName(),
otherData: MenuEventType.TRACK,
callback: true,
enable: !isNullOrUndefined(this.arianeService.getTrackId()),
}, {
position: MenuPosition.LEFT,
icon: "queue_music",
title: "true",//this.arianeService.getPlaylistName(),
otherData: MenuEventType.PLAYLIST,
callback: true,
enable: !isNullOrUndefined(this.arianeService.getPlaylistId()),
}
],
}, {
position: MenuPosition.RIGHT,
image: "assets/images/avatar_generic.svg",
title: "",
subMenu: [
{
position: MenuPosition.LEFT,
hover: `You are logged as: <b>${this.sessionService.getLogin()}</b>`,
title: `Sign in as ${this.sessionService.getLogin()}`,
}, {
position: MenuPosition.LEFT,
icon: "add_circle",
title: "Add media",
navigateTo: "upload",
enable: this.sessionService.hasRight(UserRoles222.admin),
}, {
position: MenuPosition.LEFT,
icon: "settings",
title: "Settings",
navigateTo: "settings",
}, {
position: MenuPosition.LEFT,
icon: "help",
title: "Help",
navigateTo: "help",
}, {
position: MenuPosition.LEFT,
hover: "Exit connection",
icon: "exit_to_app",
title: "Sign out",
callback: true,
otherData: MenuEventType.SSO_LOGOUT,
},
],
},
];
} else {
this.currentMenu = [
{
position: MenuPosition.LEFT,
hover: "Go to Home page",
icon: "home",
title: "Home",
navigateTo: "home",
}, {
position: MenuPosition.RIGHT,
hover: "Create a new account",
icon: "add_circle_outline",
title: "Sign-up",
callback: true,
model: this.signUpEnable ? undefined : "disable",
otherData: MenuEventType.SSO_SIGNUP,
}, {
position: MenuPosition.RIGHT,
hover: "Login page",
icon: "account_circle",
title: "Sign-in",
callback: true,
otherData: MenuEventType.SSO_LOGIN,
},
];
}
console.log(" ==> DONE");
}
getSegmentDisplayable(): string {
let segment = this.arianeService.getCurrrentSegment();
if (segment === "artist") {
return "Artists"
}
if (segment === "gender") {
return "Genders"
}
if (segment === "album") {
return "Albums"
}
if (segment === "track") {
return "Tracks"
}
if (segment === "playlist") {
return "Playlist"
}
return "";
}
}

View File

@ -1,96 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2018, Edouard DUPIN, all right reserved
* @license PROPRIETARY (see license file)
*/
import { BrowserModule } from '@angular/platform-browser';
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { HttpClientModule } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { ElementDataImageComponent } from './component/data-image/data-image';
import { ElementTypeComponent } from './component/element-type/element-type';
import { PopInCreateType } from './popin/create-type/create-type';
import { AppComponent } from './app.component';
import {
HomeScene, HelpScene, GenderScene, PlaylistScene, ArtistScene, AlbumScene, AlbumsScene, TrackScene, SettingsScene,
TrackEditScene, AlbumEditScene, ArtistEditScene, ArtistsScene, ArtistAlbumScene
} from './scene';
import { GenderService, DataService, PlaylistService, ArtistService, AlbumService, TrackService, ArianeService, PlayerService } from './service';
import { UploadScene } from './scene/upload/upload';
import { ElementSeriesComponent, ElementTrackComponent, ElementSeasonComponent, ElementVideoComponent, ElementPlayerAudioComponent, DescriptionAreaComponent } from './component';
import { KarCWModule } from '@kangaroo-and-rabbit/kar-cw';
import { environment } from 'environments/environment';
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { CommonModule } from "@angular/common";
@NgModule({
declarations: [
AppComponent,
ElementDataImageComponent,
ElementTypeComponent,
ElementSeriesComponent,
ElementTrackComponent,
ElementSeasonComponent,
ElementVideoComponent,
ElementPlayerAudioComponent,
DescriptionAreaComponent,
PopInCreateType,
HomeScene,
HelpScene,
GenderScene,
PlaylistScene,
ArtistAlbumScene,
ArtistsScene,
ArtistScene,
AlbumScene,
AlbumsScene,
TrackScene,
SettingsScene,
TrackEditScene,
AlbumEditScene,
ArtistEditScene,
UploadScene,
],
imports: [
FormsModule,
ReactiveFormsModule,
CommonModule,
BrowserModule,
RouterModule,
AppRoutingModule,
HttpClientModule,
KarCWModule,
],
providers: [
{ provide: 'ENVIRONMENT', useValue: environment },
ArianeService,
PlayerService,
GenderService,
DataService,
PlaylistService,
ArtistService,
AlbumService,
TrackService,
],
exports: [
AppComponent,
ElementTypeComponent,
ElementSeriesComponent,
ElementSeasonComponent,
ElementVideoComponent,
PopInCreateType,
],
bootstrap: [
AppComponent
],
schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA],
})
export class AppModule { }

View File

@ -1,8 +0,0 @@
/**
* API of the server (auto-generated code)
*/
import { HTTPMimeType, HTTPRequestModel, ModelResponseHttp, RESTConfig, RESTRequestJson, RESTRequestJsonArray, RESTRequestVoid } from "./rest-tools"
import {} from "./model"
export namespace Front {
}

View File

@ -1,20 +0,0 @@
/**
* API of the server (auto-generated code)
*/
import { HTTPMimeType, HTTPRequestModel, ModelResponseHttp, RESTConfig, RESTRequestJson, RESTRequestJsonArray, RESTRequestVoid } from "./rest-tools"
import {HealthResult, isHealthResult, } from "./model"
export namespace HealthCheck {
export function getHealth({ restConfig, }: {
restConfig: RESTConfig,
}): Promise<HealthResult> {
return RESTRequestJson({
restModel: {
endPoint: "/health_check",
requestType: HTTPRequestModel.GET,
accept: HTTPMimeType.JSON,
},
restConfig,
}, isHealthResult);
};
}

View File

@ -1,13 +0,0 @@
/**
* Global import of the package
*/
export * from "./model";
export * from "./album-resource";
export * from "./artist-resource";
export * from "./front";
export * from "./gender-resource";
export * from "./health-check";
export * from "./playlist-resource";
export * from "./user-resource";
export * from "./track-resource";
export * from "./data-resource";

View File

@ -1,407 +0,0 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
export const ZodUUID = zod.string().uuid();
export type UUID = zod.infer<typeof ZodUUID>;
export function isUUID(data: any): data is UUID {
try {
ZodUUID.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
export const ZodLong = zod.number();
export type Long = zod.infer<typeof ZodLong>;
export function isLong(data: any): data is Long {
try {
ZodLong.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
export const ZodInteger = zod.number().safe();
export type Integer = zod.infer<typeof ZodInteger>;
export function isInteger(data: any): data is Integer {
try {
ZodInteger.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
export const ZodFloat = zod.number();
export type Float = zod.infer<typeof ZodFloat>;
export function isFloat(data: any): data is Float {
try {
ZodFloat.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
export const ZodInstant = zod.string();
export type Instant = zod.infer<typeof ZodInstant>;
export function isInstant(data: any): data is Instant {
try {
ZodInstant.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
export const ZodDate = zod.date();
export type Date = zod.infer<typeof ZodDate>;
export function isDate(data: any): data is Date {
try {
ZodDate.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
export const ZodTimestamp = zod.date();
export type Timestamp = zod.infer<typeof ZodTimestamp>;
export function isTimestamp(data: any): data is Timestamp {
try {
ZodTimestamp.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
export const ZodLocalDate = zod.date();
export type LocalDate = zod.infer<typeof ZodLocalDate>;
export function isLocalDate(data: any): data is LocalDate {
try {
ZodLocalDate.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
export const ZodLocalTime = zod.date();
export type LocalTime = zod.infer<typeof ZodLocalTime>;
export function isLocalTime(data: any): data is LocalTime {
try {
ZodLocalTime.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
export const ZodRestErrorResponse = zod.object({
uuid: ZodUUID.optional(),
time: zod.string().max(255).optional(),
error: zod.string().max(255).optional(),
message: zod.string().max(255).optional(),
status: ZodInteger,
statusMessage: zod.string().max(255).optional()
});
export type RestErrorResponse = zod.infer<typeof ZodRestErrorResponse>;
export function isRestErrorResponse(data: any): data is RestErrorResponse {
try {
ZodRestErrorResponse.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
export const ZodGenericTiming = zod.object({
// Create time of the object
createdAt: ZodDate.readonly().optional(),
// When update the object
updatedAt: ZodDate.readonly().optional()
});
export type GenericTiming = zod.infer<typeof ZodGenericTiming>;
export function isGenericTiming(data: any): data is GenericTiming {
try {
ZodGenericTiming.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
export const ZodGenericData = ZodGenericTiming.extend({
// Unique Id of the object
id: ZodLong.readonly().optional()
});
export type GenericData = zod.infer<typeof ZodGenericData>;
export function isGenericData(data: any): data is GenericData {
try {
ZodGenericData.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
export const ZodGenericDataSoftDelete = ZodGenericData.extend({
// Deleted state
deleted: zod.boolean().readonly().optional()
});
export type GenericDataSoftDelete = zod.infer<typeof ZodGenericDataSoftDelete>;
export function isGenericDataSoftDelete(data: any): data is GenericDataSoftDelete {
try {
ZodGenericDataSoftDelete.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
export const ZodNodeSmall = ZodGenericDataSoftDelete.extend({
name: zod.string().max(256).optional(),
description: zod.string().optional(),
// List of Id of the specific covers
covers: zod.array(ZodUUID).optional()
});
export type NodeSmall = zod.infer<typeof ZodNodeSmall>;
export function isNodeSmall(data: any): data is NodeSmall {
try {
ZodNodeSmall.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
export const ZodAlbum = ZodNodeSmall.extend({
publication: ZodLocalDate.optional()
});
export type Album = zod.infer<typeof ZodAlbum>;
export function isAlbum(data: any): data is Album {
try {
ZodAlbum.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
export const ZodArtist = ZodNodeSmall.extend({
firstName: zod.string().max(256).optional(),
surname: zod.string().max(256).optional(),
birth: ZodLocalDate.optional(),
death: ZodLocalDate.optional()
});
export type Artist = zod.infer<typeof ZodArtist>;
export function isArtist(data: any): data is Artist {
try {
ZodArtist.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
export const ZodGender = ZodNodeSmall.extend({
});
export type Gender = zod.infer<typeof ZodGender>;
export function isGender(data: any): data is Gender {
try {
ZodGender.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
export const ZodHealthResult = zod.object({
});
export type HealthResult = zod.infer<typeof ZodHealthResult>;
export function isHealthResult(data: any): data is HealthResult {
try {
ZodHealthResult.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
export const ZodPlaylist = ZodNodeSmall.extend({
tracks: zod.array(ZodLong).optional()
});
export type Playlist = zod.infer<typeof ZodPlaylist>;
export function isPlaylist(data: any): data is Playlist {
try {
ZodPlaylist.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
export const ZodUser = ZodGenericDataSoftDelete.extend({
login: zod.string().max(128).optional(),
lastConnection: ZodTimestamp.optional(),
admin: zod.boolean(),
blocked: zod.boolean(),
removed: zod.boolean(),
covers: zod.array(ZodLong).optional()
});
export type User = zod.infer<typeof ZodUser>;
export function isUser(data: any): data is User {
try {
ZodUser.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
export const ZodUserKarusic = ZodUser.extend({
});
export type UserKarusic = zod.infer<typeof ZodUserKarusic>;
export function isUserKarusic(data: any): data is UserKarusic {
try {
ZodUserKarusic.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
export const ZodUserOut = zod.object({
id: ZodLong,
login: zod.string().max(255).optional()
});
export type UserOut = zod.infer<typeof ZodUserOut>;
export function isUserOut(data: any): data is UserOut {
try {
ZodUserOut.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
export const ZodTrack = ZodNodeSmall.extend({
genderId: ZodLong.optional(),
albumId: ZodLong.optional(),
track: ZodLong.optional(),
dataId: ZodUUID.optional(),
artists: zod.array(ZodLong).optional()
});
export type Track = zod.infer<typeof ZodTrack>;
export function isTrack(data: any): data is Track {
try {
ZodTrack.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
export const ZodUUIDGenericData = ZodGenericTiming.extend({
// Unique UUID of the object
id: ZodUUID.readonly().optional()
});
export type UUIDGenericData = zod.infer<typeof ZodUUIDGenericData>;
export function isUUIDGenericData(data: any): data is UUIDGenericData {
try {
ZodUUIDGenericData.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
export const ZodUUIDGenericDataSoftDelete = ZodUUIDGenericData.extend({
// Deleted state
deleted: zod.boolean().readonly().optional()
});
export type UUIDGenericDataSoftDelete = zod.infer<typeof ZodUUIDGenericDataSoftDelete>;
export function isUUIDGenericDataSoftDelete(data: any): data is UUIDGenericDataSoftDelete {
try {
ZodUUIDGenericDataSoftDelete.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data ${e}`);
return false;
}
}
export const ZodData = ZodUUIDGenericDataSoftDelete.extend({
// Sha512 of the data
sha512: zod.string().max(128).optional(),
// Mime -type of the media
mimeType: zod.string().max(128).optional(),
// Size in Byte of the data
size: ZodLong.optional()
});
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 ${e}`);
return false;
}
}

View File

@ -1,246 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2024, Edouard DUPIN, all right reserved
* @license MPL-2
*/
import { RestErrorResponse } from "./model"
export enum HTTPRequestModel {
DELETE = 'DELETE',
GET = 'GET',
PATCH = 'PATCH',
POST = 'POST',
PUT = 'PUT',
}
export enum HTTPMimeType {
ALL = '*/*',
CSV = 'text/csv',
IMAGE = 'image/*',
IMAGE_JPEG = 'image/jpeg',
IMAGE_PNG = 'image/png',
JSON = 'application/json',
MULTIPART = 'multipart/form-data',
OCTET_STREAM = 'application/octet-stream',
TEXT_PLAIN = 'text/plain',
}
export interface RESTConfig {
// base of the server: http(s)://my.server.org/plop/api/
server: string;
// Token to access of the data.
token?: string;
}
export interface RESTModel {
// base of the local API request: "sheep/{id}".
endPoint: string;
// Type of the request.
requestType?: HTTPRequestModel;
// Input type requested.
accept?: HTTPMimeType;
// Content of the local data.
contentType?: HTTPMimeType;
// Mode of the TOKEN in urk or Header
tokenInUrl?: boolean;
}
export interface ModelResponseHttp {
status: number;
data: any;
}
export function isArrayOf<TYPE>(
data: any,
typeChecker: (subData: any) => subData is TYPE,
length?: number
): data is TYPE[] {
if (!Array.isArray(data)) {
return false;
}
if (!data.every(typeChecker)) {
return false;
}
if (length !== undefined && data.length != length) {
return false;
}
return true;
}
export type RESTRequestType = {
restModel: RESTModel,
restConfig: RESTConfig,
data?: any,
params?: object,
queries?: object,
};
function removeTrailingSlashes(input: string): string {
return input.replace(/\/+$/, '');
}
function removeLeadingSlashes(input: string): string {
return input.replace(/^\/+/, '');
}
export function RESTUrl({ restModel, restConfig, data, params, queries }: RESTRequestType): string {
// Create the URL PATH:
let generateUrl = `${removeTrailingSlashes(restConfig.server)}/${removeLeadingSlashes(restModel.endPoint)}`;
if (params !== undefined) {
for (let key of Object.keys(params)) {
generateUrl = generateUrl.replaceAll(`{${key}}`, `${params[key]}`);
}
}
if (queries === undefined && (restConfig.token === undefined || restModel.tokenInUrl !== true)) {
return generateUrl;
}
const searchParams = new URLSearchParams();
if (queries !== undefined) {
for (let key of Object.keys(queries)) {
const value = queries[key];
if (Array.isArray(value)) {
for (let iii = 0; iii < value.length; iii++) {
searchParams.append(`${key}`, `${value[iii]}`);
}
} else {
searchParams.append(`${key}`, `${value}`);
}
}
}
if (restConfig.token !== undefined && restModel.tokenInUrl === true) {
searchParams.append('Authorization', `Bearer ${restConfig.token}`);
}
return generateUrl + "?" + searchParams.toString();
}
export function RESTRequest({ restModel, restConfig, data, params, queries }: RESTRequestType): Promise<ModelResponseHttp> {
// Create the URL PATH:
let generateUrl = RESTUrl({ restModel, restConfig, data, params, queries });
let headers: any = {};
if (restConfig.token !== undefined && restModel.tokenInUrl !== true) {
headers['Authorization'] = `Bearer ${restConfig.token}`;
}
if (restModel.accept !== undefined) {
headers['Accept'] = restModel.accept;
}
if (restModel.requestType !== HTTPRequestModel.GET) {
// if Get we have not a content type, the body is empty
if (restModel.contentType !== HTTPMimeType.MULTIPART) {
// special case of multi-part ==> no content type otherwise the browser does not set the ";bundary=--****"
headers['Content-Type'] = restModel.contentType;
}
}
let body = data;
if (restModel.contentType === HTTPMimeType.JSON) {
body = JSON.stringify(data);
} else if (restModel.contentType === HTTPMimeType.MULTIPART) {
const formData = new FormData();
for (const name in data) {
formData.append(name, data[name]);
}
body = formData
}
console.log(`Call ${generateUrl}`)
return new Promise((resolve, reject) => {
fetch(generateUrl, {
method: restModel.requestType,
headers,
body,
}).then((response: Response) => {
if (response.status >= 200 && response.status <= 299) {
const contentType = response.headers.get('Content-Type');
if (restModel.accept !== contentType) {
reject({
time: Date().toString(),
status: 901,
error: `REST check wrong type: ${restModel.accept} != ${contentType}`,
statusMessage: "Fetch error",
message: "rest-tools.ts Wrong type in the message return type"
} as RestErrorResponse);
} else if (contentType === HTTPMimeType.JSON) {
response
.json()
.then((value: any) => {
//console.log(`RECEIVE ==> ${response.status}=${ JSON.stringify(value, null, 2)}`);
resolve({ status: response.status, data: value });
})
.catch((reason: any) => {
reject({
time: Date().toString(),
status: 902,
error: `REST parse json fail: ${reason}`,
statusMessage: "Fetch parse error",
message: "rest-tools.ts Wrong message model to parse"
} as RestErrorResponse);
});
} else {
resolve({ status: response.status, data: response.body });
}
} else {
reject({
time: Date().toString(),
status: response.status,
error: `${response.body}`,
statusMessage: "Fetch code error",
message: "rest-tools.ts Wrong return code"
} as RestErrorResponse);
}
}).catch((error: any) => {
reject({
time: Date(),
status: 999,
error: error,
statusMessage: "Fetch catch error",
message: "http-wrapper.ts detect an error in the fetch request"
});
});
});
}
export function RESTRequestJson<TYPE>(request: RESTRequestType, checker: (data: any) => data is TYPE): Promise<TYPE> {
return new Promise((resolve, reject) => {
RESTRequest(request).then((value: ModelResponseHttp) => {
if (checker(value.data)) {
resolve(value.data);
} else {
reject({
time: Date().toString(),
status: 950,
error: "REST Fail to verify the data",
statusMessage: "API cast ERROR",
message: "api.ts Check type as fail"
} as RestErrorResponse);
}
}).catch((reason: RestErrorResponse) => {
reject(reason);
});
});
}
export function RESTRequestJsonArray<TYPE>(request: RESTRequestType, checker: (data: any) => data is TYPE): Promise<TYPE[]> {
return new Promise((resolve, reject) => {
RESTRequest(request).then((value: ModelResponseHttp) => {
if (isArrayOf(value.data, checker)) {
resolve(value.data);
} else {
reject({
time: Date().toString(),
status: 950,
error: "REST Fail to verify the data",
statusMessage: "API cast ERROR",
message: "api.ts Check type as fail"
} as RestErrorResponse);
}
}).catch((reason: RestErrorResponse) => {
reject(reason);
});
});
}
export function RESTRequestVoid(request: RESTRequestType): Promise<void> {
return new Promise((resolve, reject) => {
RESTRequest(request).then((value: ModelResponseHttp) => {
resolve();
}).catch((reason: RestErrorResponse) => {
reject(reason);
});
});
}

View File

@ -1,51 +0,0 @@
/**
* API of the server (auto-generated code)
*/
import { HTTPMimeType, HTTPRequestModel, ModelResponseHttp, RESTConfig, RESTRequestJson, RESTRequestJsonArray, RESTRequestVoid } from "./rest-tools"
import {UserOut, Long, UserKarusic, isUserOut, isUserKarusic, } from "./model"
export namespace UserResource {
export function get({ restConfig, params, produce, }: {
restConfig: RESTConfig,
params: {
id: Long,
},
produce: HTTPMimeType.JSON,
}): Promise<UserKarusic> {
return RESTRequestJson({
restModel: {
endPoint: "/users/{id}",
requestType: HTTPRequestModel.GET,
accept: produce,
},
restConfig,
params,
}, isUserKarusic);
};
export function gets({ restConfig, produce, }: {
restConfig: RESTConfig,
produce: HTTPMimeType.JSON,
}): Promise<UserKarusic[]> {
return RESTRequestJsonArray({
restModel: {
endPoint: "/users",
requestType: HTTPRequestModel.GET,
accept: produce,
},
restConfig,
}, isUserKarusic);
};
export function getMe({ restConfig, produce, }: {
restConfig: RESTConfig,
produce: HTTPMimeType.JSON,
}): Promise<UserOut> {
return RESTRequestJson({
restModel: {
endPoint: "/users/me",
requestType: HTTPRequestModel.GET,
accept: produce,
},
restConfig,
}, isUserOut);
};
}

View File

@ -1,2 +0,0 @@
<!--<canvas width="200" height="250" style="border:1px solid #d3d3d3;" id="imageCanvas" #imageCanvas></canvas>-->
<img src="{{cover}}"/>

View File

@ -1,5 +0,0 @@
img {
max-height: 250px;
max-width: 200px;
}

View File

@ -1,58 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2018, Edouard DUPIN, all right reserved
* @license PROPRIETARY (see license file)
*/
import { Component, OnInit, Input } from '@angular/core';
//import { ModelResponseHttp } from '@app/service/http-wrapper';
import { DataService } from 'app/service/data';
@Component({
selector: 'data-image',
templateUrl: './data-image.html',
styleUrls: ['./data-image.less']
})
export class ElementDataImageComponent implements OnInit {
// input parameters
@Input() id: string = "0";
cover: string = '';
/*
imageCanvas:any;
@ViewChild('imageCanvas')
set mainDivEl(el: ElementRef) {
if(el !== null && el !== undefined) {
this.imageCanvas = el.nativeElement;
}
}
*/
constructor(private dataService: DataService) {
}
ngOnInit() {
this.cover = this.dataService.getThumbnailUrl(this.id);
/*
let canvas = this.imageCanvas.nativeElement;
let ctx = canvas.getContext("2d");
console.log(`Request thumbnail for ---> ${this.id}`);
this.dataService.getImageThumbnail(this.id)
.then((result:ModelResponseHttp) => {
console.log(`plop ---> ${result.status}`);
const response = result.data as Response;
response.blob().then((value:Blob) => {
let img = new Image();
img.onload = function() {
//ctx.drawImage(img, 0, 0)
}
let imageUrl = URL.createObjectURL(value);
console.log(`get new image url blob: ${imageUrl}`);
//img.src = imageUrl;
})
}).catch(()=>{
console.log("plop ---> ");
});
//img.src = "../../assets/aCRF-PRV111_CLN-001 v1.4-images/aCRF-PRV111_CLN-001 v1.4-blank_0.jpg";
//ctx.drawImage(img, 10, 10, 250, 250);
*/
}
}

View File

@ -1,42 +0,0 @@
<div class="fill-title">
<div class="description-area">
@if(title) {
<div class="shadow-text title">
{{title}}
</div>
}
@if(name) {
<div class="shadow-text sub-title-main">
{{name}}
</div>
}
@if(description) {
<div class="description">
{{description}}
</div>
}
<div class="button-area">
<button class="circular-button" (click)="playAll($event)" type="submit">
<i class="material-icons">play_arrow</i>
</button>
<button class="circular-button" (click)="playShuffle($event)" type="submit">
<i class="material-icons">shuffle</i>
</button>
<div style="flex:1;"></div>
</div>
</div>
@if (cover1 && cover1.length > 0) {
<div class="cover-area">
<div class="cover">
<img src="{{cover1[0]}}"/>
</div>
</div>
}
@if (cover2 && cover2.length > 0) {
<div class="cover-area">
<div class="cover">
<img src="{{cover2[0]}}"/>
</div>
</div>
}
</div>

View File

@ -1,31 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2018, Edouard DUPIN, all right reserved
* @license PROPRIETARY (see license file)
*/
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'description-area',
templateUrl: './description-area.html',
styleUrls: ['./description-area.less']
})
export class DescriptionAreaComponent {
@Input() title: string;
@Input() name?: string;
@Input() description?: string;
@Input() cover1?: number[];
@Input() cover2?: number[];
@Output() play = new EventEmitter<void>();
@Output() shuffle = new EventEmitter<void>();
playShuffle(event: any): void {
this.shuffle.emit();
}
playAll(event: any): void {
this.play.emit();
}
}

View File

@ -1,52 +0,0 @@
@if(mediaSource) {
<div>
<audio width="1" height="1" src="{{mediaSource}}"
#mediaPlayer
preload
(play)="changeStateToPlay()"
(pause)="changeStateToPause()"
(timeupdate)="changeTimeupdate($event.currentTime)"
(durationchange)="changeDurationchange($event.duration)"
(loadedmetadata)="changeMetadata()"
autoplay
(ended)="onAudioEnded()"
>
</audio>
<div class="controls">
<div class="controls-inner">
<div class="title text-ellipsis">
<label class="unselectable">{{dataTitle}}</label>
</div>
<div class="text-ellipsis">
<label class="unselectable">{{dataAuthor}} / {{dataAlbum}}</label>
</div>
<div class="timer-slider">
<input type="range" min="0" class="slider"
[value]="currentTime"
[max]="duration"
(input)="seek($event.target)">
</div>
<div class="flex-row">
<label class="unselectable">{{currentTimeDisplay}}</label>
<label class="unselectable flex-row-last">{{durationDisplay}}</label>
</div>
<div class="control">
@if(!isPlaying) {
<button (click)="onPlay()"><i class="material-icons">play_arrow</i></button>
}
@else {
<button (click)="onPause()"><i class="material-icons">pause</i></button>
}
<button (click)="onStop()"><i class="material-icons">stop</i></button>
<div style="margin:auto"></div>
<button [disabled]="havePrevious" (click)="onBefore()"><i class="material-icons">navigate_before</i></button>
<button (click)="onRewind()"><i class="material-icons">fast_rewind</i></button>
<button (click)="onForward()"><i class="material-icons">fast_forward</i></button>
<button [disabled]="haveNext" (click)="onNext()"><i class="material-icons">navigate_next</i></button>
<div style="margin:auto"></div>
<button (click)="onPlayMode()"><i class="material-icons">{{playMode}}</i></button>
</div>
</div>
</div>
</div>
}

View File

@ -1,113 +0,0 @@
.controls {
//visibility: hidden;
opacity: 0.85;
width: 96%;
//height: 150px;
border-radius: 10px;
position: absolute;
bottom: 20px;
left: 2%;
//margin-left: -200px;
background-color: black;
box-shadow: 3px 3px 5px black;
transition: 1s all;
display: flex;
font-size: 40px;
min-width: 380px;
.controls-inner {
position: relative;
width: 100%;
height: 100%;
min-width: 350px;
font-family: monospace;
color: white;
margin: 10px 15px 1px 15px;
font-size: 20px;
}
.material-icons {
color: #FFF;
font-size: 40px;
}
button {
border: none;
background: none;
}
button:disabled,
button[disabled] {
.material-icons {
color: rgb(46, 46, 46);
}
}
.slider {
position: relative;
//-webkit-appearance: none;
width: 100%;
height: 10px;
top: 5px;
border-radius: 5px;
background: #d3d3d3;
outline: none;
opacity: 0.7;
-webkit-transition: .2s;
transition: opacity .2s;
flex: 5;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 25px;
height: 25px;
border-radius: 50%;
background: #4CAF50;
cursor: pointer;
}
.slider::-moz-range-thumb {
width: 25px;
height: 25px;
border-radius: 50%;
background: #4CAF50;
cursor: pointer;
}
.text-ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.title {
font-size: 24px;
font-weight: bold;
}
.timer-slider {
width: 100%;
margin: 4px 0px 4px 0px;
font-size: 20px;
padding-bottom: 10px;
}
.control {
width: 100%;
font-size: 30px;
font-weight: bold;
display: flex;
justify-content: center;
}
}
button:before {
font-family: HeydingsControlsRegular;
font-size: 30px;
position: relative;
color: #FFF;
}

View File

@ -1,359 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2018, Edouard DUPIN, all right reserved
* @license PROPRIETARY (see license file)
*/
import { Component, OnInit, ViewChild, ElementRef, Inject } from '@angular/core';
import { DataService, PlayerService, TrackService, AlbumService, ArtistService } from 'app/service';
import { PlaylistCurrent } from 'app/service/player';
import { Title } from '@angular/platform-browser';
import { Album, Artist, Track } from 'app/back-api';
import { isArray, isNullOrUndefined, Environment } from '@kangaroo-and-rabbit/kar-cw';
export enum PlayMode {
PLAY_ONE = "check",
PLAY_ALL = "trending_flat",
PLAY_ONE_LOOP = "repeat_one",
PLAY_ALL_LOOP = "repeat",
};
// note: all the input is managed with the player service ...
@Component({
selector: 'app-element-player-audio',
templateUrl: './element-player-audio.html',
styleUrls: ['./element-player-audio.less']
})
export class ElementPlayerAudioComponent implements OnInit {
mediaPlayer: HTMLAudioElement;
duration: number;
durationDisplay: string;
@ViewChild('mediaPlayer')
set mainVideoEl(el: ElementRef) {
if (el !== null && el !== undefined) {
this.mediaPlayer = el.nativeElement;
}
}
public mediaSource: string = undefined;
public name: string = 'rr';
public description: string;
public countTrack: number;
public covers: string[];
public volume_value: number = 100;
public volume_displayMenu: boolean = false;
dataTitle: string;
dataAlbum: string;
dataAuthor: string;
playStream: boolean;
isPlaying: boolean;
currentTime: any;
currentTimeDisplay: string;
havePrevious: boolean = false;
haveNext: boolean = false;
playMode: PlayMode = PlayMode.PLAY_ALL;
onPlayMode() {
if (this.playMode === PlayMode.PLAY_ONE) {
this.playMode = PlayMode.PLAY_ALL;
} else if (this.playMode === PlayMode.PLAY_ALL) {
this.playMode = PlayMode.PLAY_ONE_LOOP;
} else if (this.playMode === PlayMode.PLAY_ONE_LOOP) {
this.playMode = PlayMode.PLAY_ALL_LOOP;
} else {
this.playMode = PlayMode.PLAY_ONE;
}
}
constructor(
@Inject('ENVIRONMENT') private environment: Environment,
private playerService: PlayerService,
private trackService: TrackService,
private dataService: DataService,
private albumService: AlbumService,
private artistService: ArtistService,
private titleService: Title) {
// nothing to do...
}
private currentLMedia: Track;
private localIdStreaming: number;
private localListStreaming: number[] = [];
ngOnInit() {
/*
export interface PlaylistCurrent {
playTrackList: number[];
trackLocalId?: number;
}
*/
let self = this;
this.playerService.playChange.subscribe((newProperty: PlaylistCurrent) => {
self.localIdStreaming = newProperty.trackLocalId;
self.localListStreaming = newProperty.playTrackList;
self.updateMediaStreamed();
});
}
updateMediaStreamed() {
if (isNullOrUndefined(this.localIdStreaming)) {
this.mediaSource = undefined;
return;
}
let self = this;
this.havePrevious = this.localIdStreaming <= 0;
this.haveNext = this.localIdStreaming >= this.localListStreaming.length - 1;
this.trackService.get(this.localListStreaming[this.localIdStreaming])
.then((response: Track) => {
console.log(`get response of video : ${JSON.stringify(response, null, 2)}`);
self.currentLMedia = response;
self.dataTitle = response.name;
if (!isNullOrUndefined(response.albumId)) {
this.albumService.get(response.albumId)
.then((response2: Album) => {
self.dataAlbum = response2.name;
self.updateTitle();
})
.catch(() => { });
}
if (!isNullOrUndefined(response.artists) && isArray(response.artists) && response.artists.length > 0) {
this.artistService.get(response.artists[0])
.then((response2: Artist) => {
self.dataAuthor = response2.name;
})
.catch(() => { });
}
if (isNullOrUndefined(self.currentLMedia)) {
console.log("Can not retrieve the media ...")
return;
}
if (isNullOrUndefined(self.currentLMedia.dataId)) {
console.log("Can not retrieve the media ... null or undefined data ID")
return;
}
self.mediaSource = self.dataService.getUrl(self.currentLMedia.dataId, "unknownMediaName.webm");
}).catch((error) => {
console.error(`error not unmanaged in audio player ... 111 ${error}`);
})
}
updateTitle() {
let title = this.dataTitle;
if (!isNullOrUndefined(this.dataAlbum)) {
title = `${this.dataAlbum} - ${title}`;
}
if (!isNullOrUndefined(this.dataAuthor)) {
title = `${title} (${this.dataAuthor})`;
}
this.titleService.setTitle(`${this.environment.applName} > ${title}`)
}
changeMetadata() {
console.log('list of the stream:');
}
myPeriodicCheckFunction() {
console.log('check ... ');
}
onRequirePlay() {
this.playStream = true;
}
onRequireStop() {
this.playStream = false;
}
onAudioEnded() {
if (this.playMode === PlayMode.PLAY_ONE) {
this.playStream = false;
} else if (this.playMode === PlayMode.PLAY_ALL) {
this.onNext();
} else if (this.playMode === PlayMode.PLAY_ONE_LOOP) {
this.mediaPlayer.currentTime = 0;
this.onPlay();
} else {
if (this.localIdStreaming == this.localListStreaming.length - 1) {
this.localIdStreaming = 0;
this.updateMediaStreamed();
if (this.localListStreaming.length == 1) {
this.mediaPlayer.currentTime = 0;
this.onPlay();
}
} else {
this.onNext();
}
}
}
changeStateToPlay() {
this.isPlaying = true;
}
changeStateToPause() {
this.isPlaying = false;
}
convertInDisplayTime(time: number): string {
let temporaryValue = parseInt(`${time}`, 10);
let hours = parseInt(`${temporaryValue / 3600}`, 10);
temporaryValue = temporaryValue - hours * 3600;
let minutes = parseInt(`${temporaryValue / 60}`, 10);
let seconds = temporaryValue - minutes * 60;
let out = '';
if (hours !== 0) {
out = `${out}${hours}:`;
}
if (minutes >= 10) {
out = `${out}${minutes}:`;
} else {
out = `${out}0${minutes}:`;
}
if (seconds >= 10) {
out = out + seconds;
} else {
out = `${out}0${seconds}`;
}
return out;
}
changeTimeupdate(currentTime: any) {
// console.log("time change ");
// console.log(" ==> " + this.audioPlayer.currentTime);
this.currentTime = this.mediaPlayer.currentTime;
this.currentTimeDisplay = this.convertInDisplayTime(this.currentTime);
// console.log(" ==> " + this.currentTimeDisplay);
}
changeDurationchange(duration: any) {
console.log('duration change ');
console.log(` ==> ${this.mediaPlayer.duration}`);
this.duration = this.mediaPlayer.duration;
this.durationDisplay = this.convertInDisplayTime(this.duration);
}
onPlay() {
console.log('play');
if (isNullOrUndefined(this.mediaPlayer)) {
console.log(`error element: ${this.mediaPlayer}`);
return;
}
this.lockPlayerEnable();
this.mediaPlayer.play();
}
onPause() {
console.log('pause');
if (isNullOrUndefined(this.mediaPlayer)) {
console.log(`error element: ${this.mediaPlayer}`);
return;
}
this.unlockPlayerEnable();
this.mediaPlayer.pause();
}
onPauseToggle() {
console.log('pause toggle ...');
if (this.isPlaying === true) {
this.onPause();
} else {
this.onPlay();
}
}
onStop() {
console.log('stop');
if (this.mediaPlayer.paused && this.mediaPlayer.currentTime == 0) {
this.mediaSource = null;
return;
}
if (isNullOrUndefined(this.mediaPlayer)) {
console.log(`error element: ${this.mediaPlayer}`);
return;
}
this.unlockPlayerEnable();
this.mediaPlayer.pause();
this.mediaPlayer.currentTime = 0;
}
onBefore() {
console.log('before');
if (this.localIdStreaming <= 0) {
console.error("have no previous !!!");
return;
}
this.localIdStreaming--;
this.updateMediaStreamed();
this.playerService.previous();
}
onNext() {
console.log('next');
if (this.localIdStreaming >= this.localListStreaming.length - 1) {
console.error("have no next !!!");
return;
}
this.localIdStreaming++;
this.updateMediaStreamed();
this.playerService.next();
}
seek(newValue: any) {
console.log(`seek ${newValue.value}`);
if (isNullOrUndefined(this.mediaPlayer)) {
console.log(`error element: ${this.mediaPlayer}`);
return;
}
this.mediaPlayer.currentTime = newValue.value;
}
onRewind() {
console.log('rewind');
if (isNullOrUndefined(this.mediaPlayer)) {
console.log(`error element: ${this.mediaPlayer}`);
return;
}
this.mediaPlayer.currentTime = this.currentTime - 10;
}
onForward() {
console.log('forward');
if (isNullOrUndefined(this.mediaPlayer)) {
console.log(`error element: ${this.mediaPlayer}`);
return;
}
this.mediaPlayer.currentTime = this.currentTime + 10;
}
onMore() {
console.log('more');
if (isNullOrUndefined(this.mediaPlayer)) {
console.log(`error element: ${this.mediaPlayer}`);
return;
}
}
// these element is to permit to not stop music when sceen is off !!
wakeLock = null;
unlockPlayerEnable() {
let temporaryValue = this.wakeLock;
this.wakeLock = null;
if (!isNullOrUndefined(temporaryValue)) {
console.log(`plopppp ${temporaryValue}`);
temporaryValue.release();
}
try {
let tmp = navigator as any;
this.wakeLock = tmp.wakeLock.request('screen');
} catch (err) {
// The Wake Lock request has failed - usually system related, such as battery.
console.log(`Can not lock screen element: ${err.name}, ${err.message}`);
}
}
async lockPlayerEnable() {
if ('wakeLock' in navigator) {
try {
let tmp = navigator as any;
this.wakeLock = await tmp.wakeLock.request('screen');
} catch (err) {
// The Wake Lock request has failed - usually system related, such as battery.
console.log(`Can not lock screen element: ${err.name}, ${err.message}`);
}
}
}
}

View File

@ -1,45 +0,0 @@
<div class="imgContainer-small">
@if(covers && covers.length > 0) {
<div>
<img src="{{covers[0]}}"/>
</div>
}
@else {
<div class="noImage"></div>
}
</div>
<div class="season-small">
{{prefixName}} {{numberAlbum}}
</div>
@if(countSubType) {
<div class="description-small">
@if(!count || count == 0) {
No {{countSubType}}
}
@else {
{{count}} {{countSubType}}{{count>1?"s":""}}
}
</div>
}
@if(countSubType2) {
<div class="description-small">
@if(!count2 || count2 == 0) {
No {{countSubType2}}
}
@else {
{{count2}} {{countSubType2}}{{count2>1?"s":""}}
}
</div>
}
@if(subValueData) {
<div class="description-small">
{{subValues}}: {{subValueData}}
</div>
}
@if(!description) {
<div class="description-small">
{{description}}
</div>
}

View File

@ -1,35 +0,0 @@
.imgContainer-small {
text-align: center;
width: 100px;
margin: 4px 15px 4px 10px;
height: 100px;
img {
max-height: 100px;
max-width: 100px;
}
.noImage {
height: 95px;
width: 95px;
//box-shadow: 0px 2px 4px 0 rgba(0, 0, 0, 0.7);
border: solid 2px;
border-color: rgba(0, 0, 0, 0.7);
margin: auto;
}
float: left;
}
.title-small {
height: 50px;
//width: 100%;
font-size: 35px;
display:inline-block;
font-weight: bold;
}
.description-small {
height: 30px;
font-size: 16px;
overflow:hidden;
vertical-align: middle;
}

View File

@ -1,92 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2018, Edouard DUPIN, all right reserved
* @license PROPRIETARY (see license file)
*/
import { Component, OnInit, Input } from '@angular/core';
import { isNullOrUndefined } from '@kangaroo-and-rabbit/kar-cw';
import { NodeSmall } from 'app/back-api';
import { AlbumService, DataService } from 'app/service';
@Component({
selector: 'app-element-season',
templateUrl: './element-season.html',
styleUrls: ['./element-season.less']
})
export class ElementSeasonComponent implements OnInit {
// input parameters
@Input() element: NodeSmall;
@Input() prefix: String;
@Input() countSubTypeCallBack: (arg0: number) => Promise<number>;
@Input() countSubType: String;
@Input() countSubType2CallBack: (arg0: number) => Promise<number>;
@Input() countSubType2: String;
@Input() subValuesCallBack: (arg0: number) => Promise<string[]>;
@Input() subValues: String;
prefixName: string = "";
numberAlbum: string;
count: number;
count2: number;
subValueData: String;
covers: string[];
description: string;
constructor(
private albumService: AlbumService,
private dataService: DataService) {
}
ngOnInit() {
this.prefix = this.prefixName ?? "";
this.count = undefined;
this.count2 = undefined;
//console.log(`element: ${JSON.stringify(this.element, null, 2)}`);
if (isNullOrUndefined(this.element)) {
this.numberAlbum = undefined;
this.covers = undefined;
this.description = undefined;
}
this.numberAlbum = this.element.name;
this.description = this.element.description;
this.covers = this.dataService.getListThumbnailUrl(this.element.covers);
let self = this;
if (!isNullOrUndefined(this.countSubTypeCallBack)) {
this.countSubTypeCallBack(this.element.id)
.then((response) => {
self.count = response;
//console.log(`get values: ${this.element.id} ==> ${self.count}`)
}).catch((response) => {
self.count = undefined;
});
}
if (!isNullOrUndefined(this.countSubType2CallBack)) {
this.countSubType2CallBack(this.element.id)
.then((response) => {
self.count2 = response;
//console.log(`get values: ${this.element.id} ==> ${self.count}`)
}).catch((response) => {
self.count2 = undefined;
});
}
if (!isNullOrUndefined(this.subValuesCallBack)) {
//console.log(`Value call back define ==> call values`)
this.subValuesCallBack(this.element.id)
.then((response: string[]) => {
this.subValueData = "";
for (let kkk = 0; kkk < response.length; kkk++) {
if (kkk != 0) {
this.subValueData += ", ";
}
this.subValueData += response[kkk];
}
//console.log(`get values: ${this.element.id} ==> ${self.subValueData}`)
}).catch((response) => {
self.count2 = undefined;
});
}
}
}

View File

@ -1,23 +0,0 @@
<div>
<div class="count-base">
@if(countTrack) {
<div class="count">
{{countTrack}}
</div>
}
</div>
<div class="imgContainer-small">
@if(covers && covers.length > 0) {
<div>
<!--<data-image id="{{cover}}"></data-image>-->
<img src="{{covers[0]}}"/>
</div>
}
@else {
<div class="noImage"></div>
}
</div>
<div class="title-small">
{{name}}
</div>
</div>

View File

@ -1,63 +0,0 @@
.count-base {
height: 0px;
width: 0px;
.count {
height: 30px;
width: 30px;
font-size: 17px;
line-height: 30px;
overflow:hidden;
position:relative;
z-index: 12;
left: 165px;
top: 2px;
text-align: center;
//padding: 5px 10px;
color: rgba(0, 0, 0, 1.0);
background: rgba(256, 256, 256, 0.3);
border: 1px solid;
border-color: rgba(256, 256, 256, 0.8);
border-radius: 15px;
}
}
.imgContainer-small {
right: 2px;
top: 2px;
text-align: center;
width: 200px;
margin: 0 auto;
height: 250px;
img {
//width: 100%;
max-height: 250px;
max-width: 200px;
}
.noImage {
height: 243px;
width: 193px;
border: solid 3px;
border-color: rgba(0, 0, 0, 0.7);
margin: auto;
}
}
.title-small {
height: 40px;
width: 200px;
font-size: 17px;
overflow:hidden;
display:inline-block;
text-align: center;
padding:auto;
background: rgba(256, 256, 256, 0.3);
border-radius: 7px;
}
.description-small {
height: 30px;
font-size: 12px;
overflow:hidden;
vertical-align: middle;
}

View File

@ -1,49 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2018, Edouard DUPIN, all right reserved
* @license PROPRIETARY (see license file)
*/
import { Component, OnInit, Input } from '@angular/core';
import { DataService, TrackService } from 'app/service';
import { NodeSmall } from 'app/back-api';
import { isNullOrUndefined } from '@kangaroo-and-rabbit/kar-cw';
@Component({
selector: 'app-element-series',
templateUrl: './element-series.html',
styleUrls: ['./element-series.less']
})
export class ElementSeriesComponent implements OnInit {
// input parameters
@Input() element: NodeSmall;
name: string = 'plouf';
description: string = '';
countTrack: number = null;
covers: string[];
constructor(
private trackService: TrackService,
private dataService: DataService) {
}
ngOnInit() {
if (isNullOrUndefined(this.element)) {
this.name = '!!ERROR!!';
this.description = undefined;
return;
}
this.name = this.element.name;
this.description = this.element.description;
this.covers = this.dataService.getListThumbnailUrl(this.element.covers);
this.trackService.countTracksOfAnArtist(this.element.id)
.then((response) => {
this.countTrack = response;
}).catch((response) => {
this.countTrack = 0;
});
}
}

View File

@ -1,10 +0,0 @@
<div class="item-list-element">
<div class="season-small">
{{name}}
</div>
@if(track) {
<div class="description-small">
n° {{track}}
</div>
}
</div>

View File

@ -1,47 +0,0 @@
.item-list-element {
font-size: 20px;
padding: 1px;
height: 55px;
width: 80%;
margin: 5px auto 5px auto;
padding: 5px;
overflow: hidden;
line-height: normal;
border: none;
font-family: "Roboto","Helvetica","Arial",sans-serif;
font-weight: 500;
will-change: box-shadow;
outline: none;
cursor: pointer;
background: rgba(256, 256, 256, 0.3);
border-radius: 7px;
.material-icons {
vertical-align: middle;
}
.material-icons {
position: absolute;
top: 50%;
left: 50%;
transform: ~"translate(-12px,-12px)";
line-height: 24px;
width: 24px;
}
}
.title-small {
height: 50px;
//width: 100%;
font-size: 35px;
display:inline-block;
font-weight: bold;
}
.description-small {
height: 30px;
font-size: 16px;
overflow:hidden;
vertical-align: middle;
}

View File

@ -1,44 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2018, Edouard DUPIN, all right reserved
* @license PROPRIETARY (see license file)
*/
import { Component, OnInit, Input } from '@angular/core';
import { isNullOrUndefined } from '@kangaroo-and-rabbit/kar-cw';
import { NodeSmall } from 'app/back-api';
import { DataService } from 'app/service';
@Component({
selector: 'app-element-track',
templateUrl: './element-track.html',
styleUrls: ['./element-track.less']
})
export class ElementTrackComponent implements OnInit {
// input parameters
@Input() element: NodeSmall;
@Input() prefix: String;
prefixName: string = "";
name: string;
track: number;
covers: string[];
description: string;
constructor(
private dataService: DataService) {
}
ngOnInit() {
this.prefix = this.prefixName ?? "";
if (isNullOrUndefined(this.element)) {
this.name = undefined;
this.covers = undefined;
this.description = undefined;
}
this.name = this.element.name;
this.description = this.element.description;
this.track = this.element["track"];
this.covers = this.dataService.getListThumbnailUrl(this.element.covers);
}
}

View File

@ -1,22 +0,0 @@
<div>
@if(countTrack) {
<div class="count-base">
<span class="count">{{countTrack}}</span>
</div>
}
<div class="imgContainer-small">
@if(covers && covers.length > 0) {
<div>
<img src="{{covers[0]}}" class="miniature-small"/>
</div>
}
</div>
<div class="title-small">
{{name}}
</div>
@if(description) {
<div class="description-small">
{{description}}
</div>
}
</div>

View File

@ -1,52 +0,0 @@
.count-base {
height: 0px;
width: 100%;
right: 0px;
z-index: 12;
//position:flex;
text-align: right;
.count {
height: 30px;
//width: 30px;
font-size: 17px;
line-height: 30px;
overflow:hidden;
position:relative;
z-index: 12;
right: 2px;
top: 4px;
text-align: right;
padding: 5px 10px;
color: rgba(0, 0, 0, 1.0);
background: rgba(256, 256, 256, 0.3);
border: 1px solid;
border-color: rgba(256, 256, 256, 0.8);
border-radius: 15px;
}
}
.imgContainer-small {
text-align: center;
margin: 15px 0 0 0;
}
img.miniature-small {
width: 80px;
height: 80px;
//border-radius: 50%;
}
.title-small {
height: 60px;
font-size: 24px;
overflow:hidden;
vertical-align: middle;
}
.description-small {
height: 30px;
font-size: 12px;
overflow:hidden;
vertical-align: middle;
}

View File

@ -1,57 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2018, Edouard DUPIN, all right reserved
* @license PROPRIETARY (see license file)
*/
import { Component, OnInit, Input } from '@angular/core';
import { DataService } from 'app/service';
import { NodeSmall } from 'app/back-api';
import { isNullOrUndefined } from '@kangaroo-and-rabbit/kar-cw';
@Component({
selector: 'app-element-type',
templateUrl: './element-type.html',
styleUrls: ['./element-type.less']
})
export class ElementTypeComponent implements OnInit {
// input parameters
@Input() element: NodeSmall;
@Input() functionVignette: (number) => Promise<Number>;
public name: string = 'rr';
public description: string;
public countTrack: number;
public covers: string[];
constructor(
private dataService: DataService) {
}
ngOnInit() {
if (isNullOrUndefined(this.element)) {
this.name = 'Not a media';
this.description = undefined;
this.countTrack = undefined;
this.covers = undefined;
return;
}
let self = this;
console.log(" ??? Get element ! " + JSON.stringify(this.element));
self.name = this.element.name;
self.description = this.element.description;
self.covers = self.dataService.getListThumbnailUrl(this.element.covers);
if (!isNullOrUndefined(this.functionVignette)) {
this.functionVignette(this.element.id)
.then((response: number) => {
self.countTrack = response;
}).catch(() => {
self.countTrack = 0;
});
}
}
}

View File

@ -1,23 +0,0 @@
<div>
<div class="videoImgContainer">
@if(covers && covers.length > 0) {
<div>
<!--<data-image id="{{cover}}"></data-image>-->
<img src="{{covers[0]}}"/>
</div>
}
@else {
<div class="noImage"></div>
}
</div>
@if(element) {
<div class="title-small">
{{episodeDisplay}} {{name}}
</div>
}
@else {
<div class="title-small">
Error meda: {{element?.id}}
</div>
}
</div>

View File

@ -1,38 +0,0 @@
.videoImgContainer {
text-align: center;
width: 200px;
margin: 0 auto;
height: 250px;
img {
//width: 100%;
max-height: 250px;
max-width: 200px;
}
.noImage {
height: 243px;
width: 193px;
border: solid 3px;
border-color: rgba(0, 0, 0, 0.7);
margin: auto;
}
}
.title-small {
height: 40px;
width: 200px;
font-size: 17px;
overflow:hidden;
display:inline-block;
text-align: center;
padding:auto;
background: rgba(256, 256, 256, 0.3);
border-radius: 7px;
}
.description-small {
height: 30px;
font-size: 12px;
overflow:hidden;
vertical-align: middle;
}

Some files were not shown because too many files have changed in this diff Show More