Compare commits

...

51 Commits

Author SHA1 Message Date
Edouard DUPIN
061fc51138 [RELEASE] Release v1.2.0 2025-05-03 11:05:46 +02:00
Edouard DUPIN
c5b714b609 [FEAT] update last archidata 2025-05-03 11:04:41 +02:00
Edouard DUPIN
d23f1a945c [FEAT] upgrade to MongoDb and use ObjectId 2025-05-01 23:10:05 +02:00
50f0c903f4 [FEAT] change namespace and update to the last archidata 2025-04-14 22:21:29 +02:00
15dfdd8605 [FEAT] some intrinsec upgrade and add basic test in back 2025-03-30 23:34:45 +02:00
ba7b6e4755 [FEAT] Play all the playlist in background when phone is stop.
the explanation is the android does not support to update the GUI interface
when the application is down, but it support some update of the playing file
in the audio tag.
2025-03-23 21:01:54 +01:00
3beafab7e1 [FEAT] upgrade pependency in front lock file 2025-03-23 21:00:11 +01:00
9cf41dd094 [FIX] icon in application mode 2025-03-23 14:19:53 +01:00
4caec9a54a [FEAT] review the loggin model to be satble and efficient with user interactions
Signed-off-by: Edouard DUPIN <yui.heero@gmail.com>
2025-03-23 14:11:30 +01:00
83f8ec0e9b [FEAT] prevent application to refresh whenit is at end of page (native CSS) 2025-03-23 14:10:22 +01:00
4993c17136 [FEAT] set manifest to consider web pasge as an application 2025-03-23 14:09:36 +01:00
401e2ce3c5 [FEAT] block reload on scoll 2025-03-22 12:12:08 +01:00
693d59ab68 [FEAT] review and normailise title of the topBar
Signed-off-by: Edouard DUPIN <yui.heero@gmail.com>
2025-03-22 12:11:51 +01:00
4eff2e55ef [FIX] session management 2025-03-22 12:11:19 +01:00
1e890f9524 [FIX] remove develop mode in production 2025-03-22 12:11:06 +01:00
dbb2527cb8 [FEAT] update dependecy 2025-03-22 12:10:45 +01:00
d65faa8810 [FIX] new file update and create 2025-03-22 12:10:32 +01:00
9c9476b052 [FEAT] increase feature of topBar 2025-03-22 12:09:37 +01:00
3e92c2b74a [FEAT] update new archidata 2025-03-22 12:09:18 +01:00
a7134c01ed [FIX] readme title 2025-03-22 12:06:05 +01:00
88b27e5f39 [FIX] deploy 2025-02-11 22:23:55 +01:00
eaf0f5688e [FIX] add end correction 2025-02-11 22:06:57 +01:00
4ebfa4e2ca [FIX] form basic models 2025-02-11 21:35:42 +01:00
c65e7d5e25 [DEV] continue refacto 2025-02-11 00:10:38 +01:00
de61cc156f [DEV] update from karso 2025-02-10 21:50:04 +01:00
4d18438914 [FEAT] update new archidata 2025-02-10 19:14:12 +01:00
80dfabcf48 [FEAT] update new archidata 2025-02-10 00:46:26 +01:00
83bfeda4ca [FEAT] Chakra V3 full operational 2025-01-25 01:34:11 +01:00
c489fabb77 [theme and reciepice 2025-01-24 21:57:04 +01:00
d52052de90 [DEV] test step between receipice and direct managemnt 2025-01-20 18:45:19 +01:00
91defa42c2 [FIX] chakra ui component is not full 2025-01-14 18:56:03 +01:00
2812d21782 [FEAT] doen not work: regacto of the Chakra-ui 3.3 ==> very bad port 2025-01-13 21:59:06 +01:00
eb5a366a12 [FEAT] add on air 2025-01-11 20:48:22 +01:00
6b801d250f [FIX] remove a stupid log... 2025-01-11 19:31:51 +01:00
01fad1b9d4 [FEAT] add a shuffle in the artist list.
This feature take a random on the artist and keep the 25 first and ge a single track for each artist.
2025-01-11 19:30:01 +01:00
371bea79f9 [FEAT] add a tool to manage a shuffle of an array 2025-01-11 19:28:51 +01:00
35725e1320 [FIX] sign-in in production mode 2025-01-11 19:28:30 +01:00
d6a8c7d23f [FIX] build version 2025-01-11 18:12:27 +01:00
3c604e9593 [VERSION] update dev tag version 2025-01-11 17:33:31 +01:00
43d8108270 [RELEASE] Release v1.1.0 2025-01-11 17:33:21 +01:00
1a883193d0 [DEPENDENCY] update release of archidata 2025-01-11 17:32:43 +01:00
78b1970ba9 [FEAT] (front) Update versions of dependency 2025-01-11 17:31:01 +01:00
746d5dff96 [FIX] test will not able to connect on port 80 2025-01-11 17:31:01 +01:00
8780ea8e63 [FIX] initialization error 2025-01-11 17:31:01 +01:00
8911eed0fb [FEAT] update NCU to only update vertion but not the major 2025-01-11 17:31:01 +01:00
1a3652472e [FIX] update the error response with OID instead of UUID 2025-01-11 17:31:01 +01:00
2e62577103 [FIX] (front) fix the environment semection in production mode 2025-01-11 17:31:01 +01:00
653e77160b [FEAT] update logger to well support the backend 2025-01-11 17:31:01 +01:00
3898a6bf4f [DEV] remove old logger system 2025-01-11 17:31:01 +01:00
448cf1569b [FIX] remove old loger system 2025-01-11 17:31:01 +01:00
d3e2b3e601 [VERSION] update dev tag version 2025-01-06 23:49:35 +01:00
250 changed files with 21403 additions and 9908 deletions

1
.gitignore vendored
View File

@ -65,3 +65,4 @@ __pycache__
.design/
.vscode/
front/storybook-static
back/bin

View File

@ -6,7 +6,7 @@
FROM archlinux:base-devel AS common
# update system
RUN pacman -Syu --noconfirm && pacman-db-upgrade \
&& pacman -S --noconfirm jdk-openjdk \
&& pacman -S --noconfirm jdk-openjdk wget\
&& pacman -Scc --noconfirm
WORKDIR /tmp
@ -53,14 +53,15 @@ RUN pnpm install --prod=false
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 \
COPY \
front/tsconfig.json \
front/tsconfig.node.json \
front/vite.config.mts \
front/index.html \
./
COPY front/public ./public
COPY front/public/icons ./public/icons
COPY front/src ./src
#We are not in prod mode ==> we need to overwrite the production env.
@ -99,8 +100,14 @@ ENV LANG C.UTF-8
COPY --from=build_back /tmp/out/maven/*.jar /application/application.jar
COPY --from=build_front /tmp/dist /application/front/
# COPY front/public/icons /application/front/public/icons
# COPY front/public/icons /application/front/icons
WORKDIR /application/
EXPOSE 80
CMD ["java", "-Xms64M", "-Xmx1G", "-cp", "/application/application.jar", "org.kar.karusic.WebLauncher"]
# To verify health-check: docker inspect --format "{{json .State.Health }}" YOUR_SERVICE_NAME | jq
HEALTHCHECK --start-period=10s --start-interval=2s --interval=30s --timeout=5s --retries=10 \
CMD wget --no-verbose --tries=1 --spider http://localhost:80/api/health_check || exit 1
CMD ["java", "-Xms64M", "-Xmx1G", "-cp", "/application/application.jar", "org.atriasoft.karusic.WebLauncher"]

View File

@ -1,4 +1,4 @@
Karideo
Karusic
=======
**K**angaroo **A**nd **R**abbit (m)usic is a simple framework to propose music streaming for personal network
@ -6,40 +6,90 @@ Karideo
Run in local:
=============
so simple...
Start tools
-----------
Start the server basic interfaces: (DB(mySQL), Adminer)
```{.bash}
# start the Bdd interface (no big data > 50Mo)
cd bdd
docker-compose up -d
# start the REST API
cd back
docker-compose up -d
# start the front API
cd ../front
docker-compose up -d
docker compose -f env_dev/docker-compose.yaml up -d
```
Start the Back-end:
-------------------
convert in an angular application:
https://betterprogramming.pub/how-to-convert-your-angular-application-to-a-native-mobile-app-android-and-ios-c212b38976df
backend is developed in JAVA
The first step is configuring your JAVA version (or select the JVM with the OS)
```bash
export PATH=$(ls -d --color=never /usr/lib/jvm/java-2*-openjdk)/bin:$PATH
```
Link with the external sub-library (front)
------------------------------------------
Install the dependency:
```bash
mvn install
```
Link:
Run the test
```bash
mvn test
```
Install it for external use
```bash
mvn install
```
Execute the local server:
```bash
mvn exec:java@dev-mode
```
Start the Front-end:
--------------------
backend is developed in JAVA
```bash
cd front
pnpm run link_kar_cw
pnpm install
pnpm dev
```
un-link:
Display the result:
-------------------
[show the webpage: http://localhost:4203](http://localhost:4203)
Some other dev tools:
=====================
Format code:
------------
```bash
cd front
pnpm run unlink_kar_cw
export PATH=$(ls -d --color=never /usr/lib/jvm/java-2*-openjdk)/bin:$PATH
mvn formatter:format
mvn test
```
Tools in production mode
========================
Changing the Log Level
----------------------
In a production environment, you can adjust the log level to help diagnose bugs more effectively.
The available log levels are:
| **Log Level Tag** | **org.atriasoft.karusic** | **org.kar.archidata** | **other** |
| ----------------- | ------------------- | --------------------- | --------- |
| `prod` | INFO | INFO | INFO |
| `prod-debug` | DEBUG | INFO | INFO |
| `prod-trace` | TRACE | DEBUG | INFO |
| `prod-trace-full` | TRACE | TRACE | INFO |
| `dev` | TRACE | DEBUG | INFO |
Manual set in production:
=========================
@ -72,4 +122,4 @@ export TAG_DOCKER=latest
export REGISTRY_ADDRESS=gitea.atria-soft.org
docker build -t ${REGISTRY_ADDRESS}/kangaroo-and-rabbit/karusic:${TAG_DOCKER} .
docker push ${REGISTRY_ADDRESS}/kangaroo-and-rabbit/karusic:${TAG_DOCKER}
```
```

View File

@ -53,6 +53,9 @@ Checkstyle configuration that checks the sun coding conventions.
<module name="LambdaParameterName"/>
<module name="Regexp"/>
<module name="RegexpSinglelineJava"/>
<module name="UnusedPrivateField">
<property name="ignorePattern" value="LOGGER"/>
</module>
</module>
<module name="BeforeExecutionExclusionFileFilter">
<property name="fileNamePattern" value="module\-info\.java$"/>

View File

@ -1,7 +1,8 @@
FROM maven:3-openjdk-18 AS build
FROM maven:3-openjdk-23 AS build
COPY pom.xml /tmp/
COPY src /tmp/src/
COPY Formatter.xml /tmp/
WORKDIR /tmp/
RUN mvn clean compile assembly:single
@ -17,5 +18,5 @@ WORKDIR /application/
EXPOSE 18080
CMD ["java", "-Xms64M", "-Xmx1G", "-cp", "/application/application.jar", "org.kar.karusic.WebLauncher"]
CMD ["java", "-Xms64M", "-Xmx1G", "-cp", "/application/application.jar", "org.atriasoft.karusic.WebLauncher"]

View File

@ -11,7 +11,7 @@ mvn package
// download all dependency in out/maven/dependency
mvn dependency:copy-dependencies
java -cp out/maven/kar-karusic-0.1.0.jar org.kar.karusic.WebLauncher
java -cp out/maven/kar-karusic-0.1.0.jar org.atriasoft.karusic.WebLauncher
// create a single package jar
@ -19,7 +19,7 @@ mvn clean compile assembly:single
java -cp out/maven/karusic-0.1.0-jar-with-dependencies.jar org.kar.karusic.WebLauncher
java -cp out/maven/karusic-0.1.0-jar-with-dependencies.jar org.atriasoft.karusic.WebLauncher

View File

@ -1,26 +1,14 @@
<?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>
<groupId>org.atriasoft</groupId>
<artifactId>karusic</artifactId>
<version>1.0.4</version>
<properties>
<maven.compiler.version>3.1</maven.compiler.version>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<maven.dependency.version>3.1.1</maven.dependency.version>
</properties>
<repositories>
<repository>
<id>gitea</id>
<url>https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/maven</url>
</repository>
</repositories>
<version>1.2.0</version>
<dependencies>
<dependency>
<groupId>kangaroo-and-rabbit</groupId>
<groupId>org.atria-soft</groupId>
<artifactId>archidata</artifactId>
<version>0.20.4</version>
<version>0.30.4</version>
</dependency>
<!-- Loopback of logger JDK logging API to SLF4J -->
<dependency>
@ -39,10 +27,15 @@
<artifactId>xercesImpl</artifactId>
<version>2.12.2</version>
</dependency>
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
<version>3.1.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.18.0-rc1</version>
<version>2.18.3</version>
</dependency>
<!--
************************************************************
@ -52,24 +45,24 @@
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.11.0</version>
<version>5.12.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.11.0</version>
<version>5.12.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>2.24.1</version>
<version>2.25.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.5.0</version>
<version>3.6.0</version>
</dependency>
</dependencies>
<build>
@ -90,25 +83,54 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.version}</version>
<version>3.14.0</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<source>21</source>
<target>21</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.4.0</version>
<version>3.2.0</version>
<executions>
<execution>
<id>prod-mode</id>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>org.atriasoft.karusic.WebLauncher</mainClass>
</configuration>
</execution>
<execution>
<id>dev-mode</id>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>org.atriasoft.karusic.WebLauncherLocal</mainClass>
</configuration>
</execution>
<execution>
<id>generate-api</id>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>org.atriasoft.karusic.GenerateApi</mainClass>
</configuration>
</execution>
</executions>
<configuration>
<mainClass>org.kar.karusic.WebLauncher</mainClass>
<mainClass/>
</configuration>
</plugin>
<!-- Create the source bundle -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<version>4.0.0-beta-1</version>
<executions>
<execution>
<id>attach-sources</id>
@ -122,10 +144,12 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<version>3.2.5</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.7.1</version>
<configuration>
<archive>
<manifest>
@ -137,81 +161,21 @@
</descriptorRefs>
</configuration>
</plugin>
<!-- Create coverage -->
<!--
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.10</version>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>jacoco-check</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<element>PACKAGE</element>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>0.50</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
-->
<!-- Java-doc generation for stand-alone site -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<version>3.3.0</version>
<configuration>
<show>private</show>
<nohelp>true</nohelp>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>exec-application</id>
<phase>package</phase>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>org.kar.karusic.WebLauncher</mainClass>
</configuration>
</plugin>
<!-- Check the style of the code -->
<plugin>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>2.23.0</version>
<version>2.24.1</version>
<configuration>
<encoding>UTF-8</encoding>
<lineEnding>LF</lineEnding>
@ -242,14 +206,6 @@
<configuration>
<includeFilterFile>spotbugs-security-include.xml</includeFilterFile>
<excludeFilterFile>spotbugs-security-exclude.xml</excludeFilterFile>
<!--<plugins>
<plugin>
<groupId>com.h3xstream.findsecbugs</groupId>
<artifactId>findsecbugs-plugin</artifactId>
<version>1.12.0</version>
</plugin>
</plugins>
-->
</configuration>
</plugin>
</plugins>
@ -260,7 +216,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<version>3.3.0</version>
<configuration>
<show>public</show>
</configuration>

View File

@ -1,9 +1,9 @@
org.kar.karideo.dataTmpFolder=/application/data/tmp
org.kar.karideo.dataTmpFolder=/application/data/media
org.kar.karideo.rest.oauth=http://192.168.1.156:21080/oauth/api/
org.kar.karideo.db.host=1992.156.1.156
org.kar.karideo.db.port=20306
org.kar.karideo.db.login=root
org.kar.karideo.db.port=klkhj456gkgtkhjgvkujfhjgkjhgsdfhb3467465fgdhdesfgh
org.kar.karideo.db.name=karideo
org.kar.karideo.address=http://0.0.0.0:18080/karideo/api/
org.atriasoft.karideo.dataTmpFolder=/application/data/tmp
org.atriasoft.karideo.dataTmpFolder=/application/data/media
org.atriasoft.karideo.rest.oauth=http://192.168.1.156:21080/oauth/api/
org.atriasoft.karideo.db.host=1992.156.1.156
org.atriasoft.karideo.db.port=20306
org.atriasoft.karideo.db.login=root
org.atriasoft.karideo.db.port=klkhj456gkgtkhjgvkujfhjgkjhgsdfhb3467465fgdhdesfgh
org.atriasoft.karideo.db.name=karideo
org.atriasoft.karideo.address=http://0.0.0.0:18080/karideo/api/

View File

@ -1,4 +1,4 @@
package org.kar.karusic.CodecBson;
package org.atriasoft.karusic.CodecBson;
import java.util.UUID;

View File

@ -0,0 +1,17 @@
package org.atriasoft.karusic;
import org.atriasoft.karusic.migration.Initialization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GenerateApi {
private final static Logger LOGGER = LoggerFactory.getLogger(GenerateApi.class);
private GenerateApi() {}
public static void main(final String[] args) throws Exception {
LOGGER.info("Generate API");
Initialization.generateObjects();
LOGGER.info("STOP the REST server.");
}
}

View File

@ -1,44 +1,42 @@
package org.kar.karusic;
package org.atriasoft.karusic;
import java.net.URI;
import java.util.Iterator;
import java.util.TimeZone;
import java.util.logging.LogManager;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriter;
import org.atriasoft.archidata.UpdateJwtPublicKey;
import org.atriasoft.archidata.api.DataResource;
import org.atriasoft.archidata.api.ProxyResource;
import org.atriasoft.archidata.catcher.GenericCatcher;
import org.atriasoft.archidata.db.DbConfig;
import org.atriasoft.archidata.exception.DataAccessException;
import org.atriasoft.archidata.filter.CORSFilter;
import org.atriasoft.archidata.filter.OptionFilter;
import org.atriasoft.archidata.migration.MigrationEngine;
import org.atriasoft.archidata.tools.ConfigBaseVariable;
import org.atriasoft.archidata.tools.ContextGenericTools;
import org.atriasoft.karusic.api.AlbumResource;
import org.atriasoft.karusic.api.ArtistResource;
import org.atriasoft.karusic.api.Front;
import org.atriasoft.karusic.api.GenderResource;
import org.atriasoft.karusic.api.HealthCheck;
import org.atriasoft.karusic.api.PlaylistResource;
import org.atriasoft.karusic.api.TrackResource;
import org.atriasoft.karusic.api.UserResource;
import org.atriasoft.karusic.filter.KarusicAuthenticationFilter;
import org.atriasoft.karusic.migration.Initialization;
import org.atriasoft.karusic.migration.Migration20250427;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.kar.archidata.UpdateJwtPublicKey;
import org.kar.archidata.api.DataResource;
import org.kar.archidata.api.ProxyResource;
import org.kar.archidata.catcher.GenericCatcher;
import org.kar.archidata.db.DbConfig;
import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.filter.CORSFilter;
import org.kar.archidata.filter.OptionFilter;
import org.kar.archidata.migration.MigrationEngine;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.kar.archidata.tools.ContextGenericTools;
import org.kar.karusic.api.AlbumResource;
import org.kar.karusic.api.ArtistResource;
import org.kar.karusic.api.Front;
import org.kar.karusic.api.GenderResource;
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.filter.KarusicAuthenticationFilter;
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.kar.karusic.migration.Migration20250104;
import org.glassfish.jersey.server.validation.ValidationFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;
@ -51,6 +49,7 @@ public class WebLauncher {
protected HttpServer server = null;
public WebLauncher() {
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
ConfigBaseVariable.bdDatabase = "karusic";
}
@ -64,11 +63,7 @@ public class WebLauncher {
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());
migrationEngine.add(new Migration20250104());
migrationEngine.add(new Migration20250427());
WebLauncher.LOGGER.info("Migrate the DB [START]");
migrationEngine.migrateWaitAdmin(new DbConfig());
WebLauncher.LOGGER.info("Migrate the DB [STOP]");
@ -151,6 +146,8 @@ public class WebLauncher {
// add jackson to be discover when we are ins standalone server
rc.register(JacksonFeature.class);
// enable jersey specific validations (@Valid)
rc.register(ValidationFeature.class);
// enable this to show low level request
// rc.property(LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_SERVER, Level.WARNING.getName());

View File

@ -0,0 +1,52 @@
package org.atriasoft.karusic;
import java.util.logging.LogManager;
import org.atriasoft.archidata.exception.DataAccessException;
import org.atriasoft.archidata.tools.ConfigBaseVariable;
import org.atriasoft.karusic.migration.Initialization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;
public class WebLauncherLocal extends WebLauncher {
private static final Logger LOGGER = LoggerFactory.getLogger(WebLauncherLocal.class);
private WebLauncherLocal() {}
public static void main(final String[] args) throws Exception {
// Loop-back of logger JDK logging API to SLF4J
LogManager.getLogManager().reset();
SLF4JBridgeHandler.install();
// Generate the APIs in type-script
Initialization.generateObjects();
final WebLauncherLocal launcher = new WebLauncherLocal();
launcher.process();
LOGGER.info("end-configure the server & wait finish process:");
Thread.currentThread().join();
LOGGER.info("STOP the REST server:");
}
@Override
public void process() throws InterruptedException, DataAccessException {
if (true) {
// for local test:
ConfigBaseVariable.apiAdress = "http://0.0.0.0:19080/karusic/api/";
ConfigBaseVariable.testMode = "true";
ConfigBaseVariable.dbType = "mongo";
}
// Test fail of SSO: ConfigBaseVariable.ssoAdress = null;
try {
super.migrateDB();
} catch (final Exception e) {
e.printStackTrace();
while (true) {
LOGGER.error("============================================================================");
LOGGER.error("== Migration fail ==> waiting intervention of administrator...");
LOGGER.error("============================================================================");
Thread.sleep(60 * 60 * 1000);
}
}
super.process();
}
}

View File

@ -1,31 +1,29 @@
package org.kar.karusic.api;
package org.atriasoft.karusic.api;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;
import org.atriasoft.archidata.annotation.apiGenerator.ApiInputOptional;
import org.atriasoft.archidata.annotation.apiGenerator.ApiTypeScriptProgress;
import org.atriasoft.archidata.dataAccess.DBAccess;
import org.atriasoft.archidata.dataAccess.DataAccess;
import org.atriasoft.archidata.dataAccess.addOnSQL.AddOnDataJson;
import org.atriasoft.archidata.tools.DataTools;
import org.atriasoft.karusic.model.Album;
import org.bson.types.ObjectId;
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.DBAccess;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.addOnSQL.AddOnDataJson;
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;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.security.RolesAllowed;
import jakarta.validation.Valid;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.PATCH;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
@ -35,14 +33,13 @@ import jakarta.ws.rs.core.MediaType;
@Produces({ MediaType.APPLICATION_JSON })
public class AlbumResource {
private static final Logger LOGGER = LoggerFactory.getLogger(AlbumResource.class);
static final AlbumChecker CHECKER = new AlbumChecker();
@GET
@Path("{id}")
@Path("{oid}")
@RolesAllowed("USER")
@Operation(description = "Get a specific Album with his ID")
public Album get(@PathParam("id") final Long id) throws Exception {
return DataAccess.get(Album.class, id);
public Album get(@PathParam("oid") final ObjectId oid) throws Exception {
return DataAccess.get(Album.class, oid);
// return this.morphiaService.getDatastore().find(Album.class).filter(Filters.eq("id", id)).first();
}
@ -59,30 +56,30 @@ public class AlbumResource {
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
@Operation(description = "Add an album (when all the data already exist)")
public Album post(final Album data) throws Exception {
public Album post(@Valid 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 DataAccess.insert(data, new CheckFunction(CHECKER));
return DataAccess.insert(data);
}
@PATCH
@Path("{id}")
@PUT
@Path("{oid}")
@RolesAllowed("ADMIN")
@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 {
public Album put(@PathParam("oid") final ObjectId oid, @Valid 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", master.getName());
// this.morphiaService.getDatastore().update(query, ops);
// return Response.ok(master).build();
DataAccess.updateWithJson(Album.class, id, jsonRequest, new CheckFunction(CHECKER));
return DataAccess.get(Album.class, id);
album.oid = oid;
DataAccess.update(album, oid);
return DataAccess.get(Album.class, oid);
}
// @PUT
@ -90,7 +87,7 @@ public class AlbumResource {
// @RolesAllowed("ADMIN")
// @Consumes(MediaType.APPLICATION_JSON)
// @Operation(description = "Update a specific album")
// public Album put(@PathParam("id") final Long id, final Album album)
// public Album put(@PathParam("id") final ObjectId oid, 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)
@ -100,11 +97,11 @@ public class AlbumResource {
// }
@DELETE
@Path("{id}")
@Path("{oid}")
@RolesAllowed("ADMIN")
@Operation(description = "Remove a specific album")
public void remove(@PathParam("id") final Long id) throws Exception {
DataAccess.delete(Album.class, id);
public void remove(@PathParam("oid") final ObjectId oid) throws Exception {
DataAccess.delete(Album.class, oid);
// this.morphiaService.getDatastore().find(Album.class).filter(Filters.eq("id", id)).delete();
}
@ -112,34 +109,34 @@ public class AlbumResource {
* @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 {
* @Operation(description = "Add a Track on a specific album") public Album addTrack(@PathParam("id") final ObjectId oid, @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")
@Path("{oid}/cover")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@Operation(description = "Add a cover on a specific album")
@TypeScriptProgress
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 {
@ApiTypeScriptProgress
public Album uploadCover(@PathParam("oid") final ObjectId oid, @ApiInputOptional @FormDataParam("uri") final String uri, @ApiInputOptional @FormDataParam("file") final InputStream fileInputStream,
@ApiInputOptional @FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
try (DBAccess db = DBAccess.createInterface()) {
if (uri != null) {
DataTools.uploadCoverFromUri(db, Album.class, id, uri);
DataTools.uploadCoverFromUri(db, Album.class, oid, uri);
} else {
DataTools.uploadCover(db, Album.class, id, fileInputStream, fileMetaData);
DataTools.uploadCover(db, Album.class, oid, fileInputStream, fileMetaData);
}
return db.get(Album.class, id);
return db.get(Album.class, oid);
}
}
@DELETE
@Path("{id}/cover/{coverId}")
@Path("{oid}/cover/{coverId}")
@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 {
public Album removeCover(@PathParam("oid") final ObjectId oid, @PathParam("coverId") final ObjectId coverId) throws Exception {
try (DBAccess db = DBAccess.createInterface()) {
AddOnDataJson.removeLink(db, Album.class, "id", id, "covers", coverId);
return db.get(Album.class, id);
AddOnDataJson.removeLink(db, Album.class, "id", oid, "covers", coverId);
return db.get(Album.class, oid);
}
}
}

View File

@ -0,0 +1,99 @@
package org.atriasoft.karusic.api;
import java.io.InputStream;
import java.util.List;
import org.atriasoft.archidata.annotation.apiGenerator.ApiInputOptional;
import org.atriasoft.archidata.annotation.apiGenerator.ApiTypeScriptProgress;
import org.atriasoft.archidata.dataAccess.DBAccess;
import org.atriasoft.archidata.dataAccess.DataAccess;
import org.atriasoft.archidata.dataAccess.addOnSQL.AddOnDataJson;
import org.atriasoft.archidata.tools.DataTools;
import org.atriasoft.karusic.model.Artist;
import org.bson.types.ObjectId;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.annotation.security.RolesAllowed;
import jakarta.validation.Valid;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/artist")
@Produces({ MediaType.APPLICATION_JSON })
public class ArtistResource {
private static final Logger LOGGER = LoggerFactory.getLogger(ArtistResource.class);
@GET
@Path("{oid}")
@RolesAllowed("USER")
public Artist get(@PathParam("oid") final ObjectId oid) throws Exception {
return DataAccess.get(Artist.class, oid);
}
@GET
@RolesAllowed("USER")
public List<Artist> gets() throws Exception {
return DataAccess.gets(Artist.class);
}
@POST
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Artist post(@Valid final Artist data) throws Exception {
return DataAccess.insert(data);
}
@PUT
@Path("{oid}")
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Artist put(@PathParam("oid") final ObjectId oid, @Valid final Artist artist) throws Exception {
artist.oid = oid;
DataAccess.update(artist, oid);
return DataAccess.get(Artist.class, oid);
}
@DELETE
@Path("{oid}")
@RolesAllowed("ADMIN")
public void remove(@PathParam("oid") final ObjectId oid) throws Exception {
DataAccess.delete(Artist.class, oid);
}
@POST
@Path("{oid}/cover")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@ApiTypeScriptProgress
public Artist uploadCover(@PathParam("oid") final ObjectId oid, @ApiInputOptional @FormDataParam("uri") final String uri,
@ApiInputOptional @FormDataParam("file") final InputStream fileInputStream, @ApiInputOptional @FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
try (DBAccess db = DBAccess.createInterface()) {
if (uri != null) {
DataTools.uploadCoverFromUri(db, Artist.class, oid, uri);
} else {
DataTools.uploadCover(db, Artist.class, oid, fileInputStream, fileMetaData);
}
return db.get(Artist.class, oid);
}
}
@DELETE
@Path("{oid}/cover/{coverId}")
@RolesAllowed("ADMIN")
public Artist removeCover(@PathParam("oid") final ObjectId oid, @PathParam("coverId") final ObjectId coverId) throws Exception {
try (DBAccess db = DBAccess.createInterface()) {
AddOnDataJson.removeLink(db, Artist.class, "id", oid, "covers", coverId);
return db.get(Artist.class, oid);
}
}
}

View File

@ -1,7 +1,7 @@
package org.kar.karusic.api;
package org.atriasoft.karusic.api;
import org.kar.archidata.api.FrontGeneric;
import org.kar.karusic.util.ConfigVariable;
import org.atriasoft.archidata.api.FrontGeneric;
import org.atriasoft.karusic.util.ConfigVariable;
import jakarta.ws.rs.Path;

View File

@ -0,0 +1,99 @@
package org.atriasoft.karusic.api;
import java.io.InputStream;
import java.util.List;
import org.atriasoft.archidata.annotation.apiGenerator.ApiInputOptional;
import org.atriasoft.archidata.annotation.apiGenerator.ApiTypeScriptProgress;
import org.atriasoft.archidata.dataAccess.DBAccess;
import org.atriasoft.archidata.dataAccess.DataAccess;
import org.atriasoft.archidata.dataAccess.addOnSQL.AddOnDataJson;
import org.atriasoft.archidata.tools.DataTools;
import org.atriasoft.karusic.model.Gender;
import org.bson.types.ObjectId;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.annotation.security.RolesAllowed;
import jakarta.validation.Valid;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/gender")
@Produces({ MediaType.APPLICATION_JSON })
public class GenderResource {
private static final Logger LOGGER = LoggerFactory.getLogger(GenderResource.class);
@GET
@Path("{oid}")
@RolesAllowed("USER")
public Gender get(@PathParam("oid") final ObjectId oid) throws Exception {
return DataAccess.get(Gender.class, oid);
}
@GET
@RolesAllowed("USER")
public List<Gender> gets() throws Exception {
return DataAccess.gets(Gender.class);
}
@POST
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Gender post(@Valid final Gender data) throws Exception {
return DataAccess.insert(data);
}
@PUT
@Path("{oid}")
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Gender patch(@PathParam("oid") final ObjectId oid, @Valid final Gender gender) throws Exception {
gender.oid = oid;
DataAccess.update(gender, oid);
return DataAccess.get(Gender.class, oid);
}
@DELETE
@Path("{oid}")
@RolesAllowed("ADMIN")
public void remove(@PathParam("oid") final ObjectId oid) throws Exception {
DataAccess.delete(Gender.class, oid);
}
@POST
@Path("{oid}/cover")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@ApiTypeScriptProgress
public Gender uploadCover(@PathParam("oid") final ObjectId oid, @ApiInputOptional @FormDataParam("uri") final String uri,
@ApiInputOptional @FormDataParam("file") final InputStream fileInputStream, @ApiInputOptional @FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
try (DBAccess db = DBAccess.createInterface()) {
if (uri != null) {
DataTools.uploadCoverFromUri(db, Gender.class, oid, uri);
} else {
DataTools.uploadCover(db, Gender.class, oid, fileInputStream, fileMetaData);
}
return db.get(Gender.class, oid);
}
}
@DELETE
@Path("{oid}/cover/{coverId}")
@RolesAllowed("ADMIN")
public Gender removeCover(@PathParam("oid") final ObjectId oid, @PathParam("coverId") final ObjectId coverId) throws Exception {
try (DBAccess db = DBAccess.createInterface()) {
AddOnDataJson.removeLink(db, Gender.class, "_id", oid, "covers", coverId);
return db.get(Gender.class, oid);
}
}
}

View File

@ -1,8 +1,8 @@
package org.kar.karusic.api;
package org.atriasoft.karusic.api;
import org.kar.archidata.exception.FailException;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.kar.archidata.tools.JWTWrapper;
import org.atriasoft.archidata.exception.FailException;
import org.atriasoft.archidata.tools.ConfigBaseVariable;
import org.atriasoft.archidata.tools.JWTWrapper;
import jakarta.annotation.security.PermitAll;
import jakarta.ws.rs.GET;

View File

@ -0,0 +1,93 @@
package org.atriasoft.karusic.api;
import java.io.InputStream;
import java.util.List;
import org.atriasoft.archidata.annotation.apiGenerator.ApiAsyncType;
import org.atriasoft.archidata.dataAccess.DBAccess;
import org.atriasoft.archidata.dataAccess.DataAccess;
import org.atriasoft.archidata.dataAccess.addOnSQL.AddOnDataJson;
import org.atriasoft.archidata.tools.DataTools;
import org.atriasoft.karusic.model.Playlist;
import org.bson.types.ObjectId;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.annotation.security.RolesAllowed;
import jakarta.validation.Valid;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/playlist")
@Produces({ MediaType.APPLICATION_JSON })
public class PlaylistResource {
private static final Logger LOGGER = LoggerFactory.getLogger(PlaylistResource.class);
@GET
@Path("{id}")
@RolesAllowed("USER")
public Playlist get(@PathParam("id") final ObjectId oid) throws Exception {
return DataAccess.get(Playlist.class, oid);
}
@GET
@RolesAllowed("USER")
public List<Playlist> gets() throws Exception {
return DataAccess.gets(Playlist.class);
}
@POST
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Playlist post(@Valid final Playlist data) throws Exception {
return DataAccess.insert(data);
}
@PUT
@Path("{oid}")
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Playlist put(@PathParam("oid") final ObjectId oid, @Valid final Playlist playlist) throws Exception {
playlist.oid = oid;
DataAccess.update(playlist, oid);
return DataAccess.get(Playlist.class, oid);
}
@DELETE
@Path("{oid}")
@RolesAllowed("ADMIN")
public void remove(@PathParam("oid") final ObjectId oid) throws Exception {
DataAccess.delete(Playlist.class, oid);
}
@POST
@Path("{oid}/cover")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@ApiAsyncType(Playlist.class)
public void uploadCover(@PathParam("oid") final ObjectId oid, @FormDataParam("file") final InputStream fileInputStream, @FormDataParam("file") final FormDataContentDisposition fileMetaData)
throws Exception {
try (DBAccess db = DBAccess.createInterface()) {
DataTools.uploadCover(db, Playlist.class, oid, fileInputStream, fileMetaData);
}
}
@DELETE
@Path("{oid}/cover/{coverId}")
@RolesAllowed("ADMIN")
public Playlist removeCover(@PathParam("oid") final ObjectId oid, @PathParam("coverId") final ObjectId coverId) throws Exception {
try (DBAccess db = DBAccess.createInterface()) {
AddOnDataJson.removeLink(db, Playlist.class, "id", oid, "covers", coverId);
return DataAccess.get(Playlist.class, oid);
}
}
}

View File

@ -1,4 +1,4 @@
package org.kar.karusic.api;
package org.atriasoft.karusic.api;
import java.io.IOException;
import java.io.InputStream;
@ -6,29 +6,28 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.atriasoft.archidata.annotation.apiGenerator.ApiAsyncType;
import org.atriasoft.archidata.annotation.apiGenerator.ApiInputOptional;
import org.atriasoft.archidata.annotation.apiGenerator.ApiTypeScriptProgress;
import org.atriasoft.archidata.dataAccess.DBAccess;
import org.atriasoft.archidata.dataAccess.DataAccess;
import org.atriasoft.archidata.dataAccess.addOnSQL.AddOnDataJson;
import org.atriasoft.archidata.model.Data;
import org.atriasoft.archidata.tools.DataTools;
import org.atriasoft.karusic.model.Track;
import org.bson.types.ObjectId;
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.DBAccess;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.addOnSQL.AddOnDataJson;
import org.kar.archidata.dataAccess.options.CheckFunction;
import org.kar.archidata.model.Data;
import org.kar.archidata.tools.DataTools;
import org.kar.karusic.model.Track;
import org.kar.karusic.model.Track.TrackChecker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.annotation.security.RolesAllowed;
import jakarta.validation.Valid;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.PATCH;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
@ -39,13 +38,12 @@ 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();
@GET
@Path("{id}")
@Path("{oid}")
@RolesAllowed("USER")
public Track get(@PathParam("id") final Long id) throws Exception {
return DataAccess.get(Track.class, id);
public Track get(@PathParam("oid") final ObjectId oid) throws Exception {
return DataAccess.get(Track.class, oid);
}
@GET
@ -57,71 +55,51 @@ public class TrackResource {
@POST
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Track post(final Track data) throws Exception {
return DataAccess.insert(data, new CheckFunction(CHECKER));
public Track post(@Valid final Track data) throws Exception {
return DataAccess.insert(data);
}
@PATCH
@Path("{id}")
@PUT
@Path("{oid}")
@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, new CheckFunction(CHECKER));
return DataAccess.get(Track.class, id);
public Track put(@PathParam("oid") final ObjectId oid, @Valid final Track track) throws Exception {
track.oid = oid;
DataAccess.update(track, oid);
return DataAccess.get(Track.class, oid);
}
@DELETE
@Path("{id}")
@Path("{oid}")
@RolesAllowed("ADMIN")
public void remove(@PathParam("id") final Long id) throws Exception {
DataAccess.delete(Track.class, id);
public void remove(@PathParam("oid") final ObjectId oid) throws Exception {
DataAccess.delete(Track.class, oid);
}
@POST
@Path("{id}/artist/{artistId}")
@Path("{oid}/cover")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
public Track addTrack(@PathParam("id") final Long id, @PathParam("artistId") final Long artistId) throws Exception {
try (DBAccess db = DBAccess.createInterface()) {
AddOnDataJson.removeLink(db, Track.class, "id", id, "artist", artistId);
return DataAccess.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 {
try (DBAccess db = DBAccess.createInterface()) {
AddOnDataJson.removeLink(db, Track.class, "id", id, "artist", artistId);
return DataAccess.get(Track.class, id);
}
}
@POST
@Path("{id}/cover")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@TypeScriptProgress
public Track uploadCover(@PathParam("id") final Long id, @FormDataParam("uri") final String uri, @FormDataParam("file") final InputStream fileInputStream,
@ApiTypeScriptProgress
public Track uploadCover(@PathParam("oid") final ObjectId oid, @FormDataParam("uri") final String uri, @FormDataParam("file") final InputStream fileInputStream,
@FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
try (DBAccess db = DBAccess.createInterface()) {
if (uri != null) {
DataTools.uploadCoverFromUri(db, Track.class, id, uri);
DataTools.uploadCoverFromUri(db, Track.class, oid, uri);
} else {
DataTools.uploadCover(db, Track.class, id, fileInputStream, fileMetaData);
DataTools.uploadCover(db, Track.class, oid, fileInputStream, fileMetaData);
}
return DataAccess.get(Track.class, id);
return DataAccess.get(Track.class, oid);
}
}
@DELETE
@Path("{id}/cover/{coverId}")
@Path("{oid}/cover/{coverId}")
@RolesAllowed("ADMIN")
public Track removeCover(@PathParam("id") final Long id, @PathParam("coverId") final ObjectId coverId) throws Exception {
public Track removeCover(@PathParam("oid") final ObjectId oid, @PathParam("coverId") final ObjectId coverId) throws Exception {
try (DBAccess db = DBAccess.createInterface()) {
AddOnDataJson.removeLink(db, Track.class, "id", id, "covers", coverId);
return db.get(Track.class, id);
AddOnDataJson.removeLink(db, Track.class, "_id", oid, "covers", coverId);
return db.get(Track.class, oid);
}
}
@ -129,14 +107,14 @@ public class TrackResource {
@Path("upload/")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@AsyncType(Track.class)
@TypeScriptProgress
@ApiAsyncType(Track.class)
@ApiTypeScriptProgress
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, //
@ApiInputOptional @ApiAsyncType(ObjectId.class) @FormDataParam("genderId") String genderId, //
@ApiInputOptional @ApiAsyncType(ObjectId.class) @FormDataParam("artistId") String artistId, //
@ApiInputOptional @ApiAsyncType(ObjectId.class) @FormDataParam("albumId") String albumId, //
@ApiInputOptional @ApiAsyncType(Long.class) @FormDataParam("trackId") String trackId, //
@FormDataParam("file") final InputStream fileInputStream, //
@FormDataParam("file") final FormDataContentDisposition fileMetaData //
) {
@ -157,7 +135,7 @@ public class TrackResource {
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("Missing Input 'type'"). type("text/plain"). build(); } */
final long tmpUID = DataTools.getTmpDataId();
final String sha512 = DataTools.saveTemporaryFile(fileInputStream, tmpUID);
@ -188,15 +166,17 @@ public class TrackResource {
Track trackElem = new Track();
trackElem.name = title;
trackElem.track = trackId != null ? Long.parseLong(trackId) : null;
trackElem.albumId = albumId != null ? Long.parseLong(albumId) : null;
trackElem.genderId = genderId != null ? Long.parseLong(genderId) : null;
trackElem.albumId = albumId != null ? new ObjectId(albumId) : null;
trackElem.genderId = genderId != null ? new ObjectId(genderId) : null;
trackElem.dataId = data.oid;
// Now list of artist has an internal management:
if (artistId != null) {
trackElem.artists = new ArrayList<>();
trackElem.artists.add(artistId != null ? Long.parseLong(artistId) : null);
trackElem.artists.add(artistId != null ? new ObjectId(artistId) : null);
}
trackElem = DataAccess.insert(trackElem, new CheckFunction(CHECKER));
// TODO: maybe validate here ....
trackElem = DataAccess.insert(trackElem);
/* 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) {

View File

@ -1,13 +1,13 @@
package org.kar.karusic.api;
package org.atriasoft.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.atriasoft.archidata.dataAccess.DataAccess;
import org.atriasoft.archidata.filter.GenericContext;
import org.atriasoft.karusic.api.UserResourceModel.PartRight;
import org.atriasoft.karusic.api.UserResourceModel.UserMe;
import org.atriasoft.karusic.model.UserKarusic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@ -1,10 +1,7 @@
package org.kar.karusic.api.UserResourceModel;
package org.atriasoft.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;

View File

@ -1,4 +1,4 @@
package org.kar.karusic.api.UserResourceModel;
package org.atriasoft.karusic.api.UserResourceModel;
import com.fasterxml.jackson.annotation.JsonValue;

View File

@ -1,12 +1,9 @@
package org.kar.karusic.api.UserResourceModel;
package org.atriasoft.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;

View File

@ -1,6 +1,6 @@
package org.kar.karusic.filter;
package org.atriasoft.karusic.filter;
import org.kar.archidata.filter.AuthenticationFilter;
import org.atriasoft.archidata.filter.AuthenticationFilter;
import jakarta.ws.rs.Priorities;
import jakarta.ws.rs.ext.Provider;

View File

@ -0,0 +1,112 @@
package org.atriasoft.karusic.migration;
import java.util.List;
import org.atriasoft.archidata.api.DataResource;
import org.atriasoft.archidata.api.ProxyResource;
import org.atriasoft.archidata.dataAccess.DBAccess;
import org.atriasoft.archidata.externalRestApi.AnalyzeApi;
import org.atriasoft.archidata.externalRestApi.TsGenerateApi;
import org.atriasoft.archidata.filter.PartRight;
import org.atriasoft.archidata.migration.MigrationSqlStep;
import org.atriasoft.archidata.model.Data;
import org.atriasoft.archidata.model.User;
import org.atriasoft.archidata.model.token.JwtToken;
import org.atriasoft.karusic.api.AlbumResource;
import org.atriasoft.karusic.api.ArtistResource;
import org.atriasoft.karusic.api.Front;
import org.atriasoft.karusic.api.GenderResource;
import org.atriasoft.karusic.api.HealthCheck;
import org.atriasoft.karusic.api.PlaylistResource;
import org.atriasoft.karusic.api.TrackResource;
import org.atriasoft.karusic.api.UserResource;
import org.atriasoft.karusic.model.Album;
import org.atriasoft.karusic.model.Artist;
import org.atriasoft.karusic.model.Gender;
import org.atriasoft.karusic.model.Playlist;
import org.atriasoft.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);
@Override
public String getName() {
return "Initialization";
}
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);
api.addModel(JwtToken.class);
api.addModel(PartRight.class);
TsGenerateApi.generateApi(api, "../front/src/back-api/");
LOGGER.info("Generate APIs (DONE)");
}
@Override
public void generateStep() throws Exception {
for (final Class<?> clazz : CLASSES_BASE) {
addClass(clazz);
}
addAction((final DBAccess da) -> {
final List<Gender> data = List.of(//
new Gender("Variété française"), //
new Gender("Pop"), //
new Gender("inconnue"), //
new Gender("Disco"), //
new Gender("Enfants"), //
new Gender("Portugaise"), //
new Gender("Apprentissage"), //
new Gender("Blues"), //
new Gender("Jazz"), //
new Gender("Chanson Noël"), //
new Gender("DubStep"), //
new Gender("Rap français"), //
new Gender("Classique"), //
new Gender("Rock"), //
new Gender("Electro"), //
new Gender("Celtique"), //
new Gender("Country"), //
new Gender("Variété Québéquoise"), //
new Gender("Médiéval"), //
new Gender("Variété Italienne"), //
new Gender("Comédie Musicale"), //
new Gender("Vianney"), //
new Gender("Bande Original"), //
new Gender("Bande Originale"), //
new Gender("Variété Belge"), //
new Gender("Gospel"));
da.insertMultiple(data);
});
}
public static void dropAll(final DBAccess 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 DBAccess 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

@ -1,6 +1,6 @@
package org.kar.karusic.migration;
package org.atriasoft.karusic.migration;
import org.kar.archidata.migration.MigrationSqlStep;
import org.atriasoft.archidata.migration.MigrationSqlStep;
public class Migration20231126 extends MigrationSqlStep {

View File

@ -1,6 +1,6 @@
package org.kar.karusic.migration;
package org.atriasoft.karusic.migration;
import org.kar.archidata.migration.MigrationSqlStep;
import org.atriasoft.archidata.migration.MigrationSqlStep;
public class Migration20240225 extends MigrationSqlStep {

View File

@ -1,6 +1,6 @@
package org.kar.karusic.migration;
package org.atriasoft.karusic.migration;
import org.kar.archidata.migration.MigrationSqlStep;
import org.atriasoft.archidata.migration.MigrationSqlStep;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@ -1,6 +1,6 @@
package org.kar.karusic.migration;
package org.atriasoft.karusic.migration;
import org.kar.archidata.migration.MigrationSqlStep;
import org.atriasoft.archidata.migration.MigrationSqlStep;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@ -1,4 +1,4 @@
package org.kar.karusic.migration;
package org.atriasoft.karusic.migration;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
@ -9,14 +9,14 @@ import java.util.List;
import java.util.UUID;
import org.bson.types.ObjectId;
import org.kar.archidata.api.DataResource;
import org.kar.archidata.dataAccess.DBAccess;
import org.kar.archidata.dataAccess.options.AccessDeletedItems;
import org.kar.archidata.dataAccess.options.OverrideTableName;
import org.kar.archidata.migration.MigrationSqlStep;
import org.kar.karusic.migration.model.CoverConversion;
import org.kar.karusic.migration.model.MediaConversion;
import org.kar.karusic.migration.model.OIDConversion;
import org.atriasoft.archidata.api.DataResource;
import org.atriasoft.archidata.dataAccess.DBAccess;
import org.atriasoft.archidata.dataAccess.options.AccessDeletedItems;
import org.atriasoft.archidata.dataAccess.options.OverrideTableName;
import org.atriasoft.archidata.migration.MigrationSqlStep;
import org.atriasoft.karusic.migration.model.CoverConversion;
import org.atriasoft.karusic.migration.model.MediaConversion;
import org.atriasoft.karusic.migration.model.OIDConversion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@ -0,0 +1,30 @@
package org.atriasoft.karusic.migration;
import org.atriasoft.archidata.migration.MigrationSqlStep;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Migration20250414 extends MigrationSqlStep {
private static final Logger LOGGER = LoggerFactory.getLogger(Migration20240226.class);
public static final int KARSO_INITIALISATION_ID = 1;
@Override
public String getName() {
return "migration-2025-04-14: update constraints";
}
@Override
public void generateStep() throws Exception {
addAction("""
ALTER TABLE `artist`
CHANGE `birth` `birth` timestamp(3) NULL AFTER `surname`,
CHANGE `death` `death` timestamp(3) NULL AFTER `birth`;
""");
addAction("""
ALTER TABLE `album`
CHANGE `publication` `publication` timestamp(3) NULL AFTER `description`;
""");
}
}

View File

@ -0,0 +1,148 @@
package org.atriasoft.karusic.migration;
import java.util.ArrayList;
import java.util.List;
import org.atriasoft.archidata.dataAccess.DBAccess;
import org.atriasoft.archidata.dataAccess.options.AccessDeletedItems;
import org.atriasoft.archidata.dataAccess.options.DirectData;
import org.atriasoft.archidata.dataAccess.options.ReadAllColumn;
import org.atriasoft.archidata.db.DbConfig;
import org.atriasoft.archidata.migration.MigrationSqlStep;
import org.atriasoft.archidata.model.Data;
import org.atriasoft.archidata.tools.ConfigBaseVariable;
import org.atriasoft.karusic.model.Album;
import org.atriasoft.karusic.model.Artist;
import org.atriasoft.karusic.model.Gender;
import org.atriasoft.karusic.model.Track;
import org.atriasoft.karusic.modelOld.AlbumOld;
import org.atriasoft.karusic.modelOld.ArtistOld;
import org.atriasoft.karusic.modelOld.GenderOld;
import org.atriasoft.karusic.modelOld.TrackOld;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Migration20250427 extends MigrationSqlStep {
private static final Logger LOGGER = LoggerFactory.getLogger(Migration20240226.class);
public static final int KARSO_INITIALISATION_ID = 1;
@Override
public String getName() {
return "migration-2025-04-27: Migrate to MongoDB";
}
public static ObjectId getElementArtist(final List<ArtistOld> datas, final Long id) throws Exception {
if (id == null) {
return null;
}
for (final var elem : datas) {
if (elem.id.equals(id)) {
return elem.getOid();
}
}
throw new Exception("Does not exist");
}
public static ObjectId getElementAlbum(final List<AlbumOld> datas, final Long id) throws Exception {
if (id == null) {
return null;
}
for (final var elem : datas) {
if (elem.id.equals(id)) {
return elem.getOid();
}
}
throw new Exception("Does not exist");
}
public static ObjectId getElementGender(final List<GenderOld> datas, final Long id) throws Exception {
if (id == null) {
return null;
}
for (final var elem : datas) {
if (elem.id.equals(id)) {
return elem.getOid();
}
}
throw new Exception("Does not exist");
}
@Override
public void generateStep() throws Exception {
addAction((final DBAccess daMongo) -> {
// Create the previous connection on SQL:
final DbConfig configSQL = new DbConfig("mysql", ConfigBaseVariable.getDBHost(), (short) 3906,
// final DbConfig config = new DbConfig("mysql", "db", (short) 3306,
ConfigBaseVariable.getDBLogin(), ConfigBaseVariable.getDBPassword(), ConfigBaseVariable.getDBName(), ConfigBaseVariable.getDBKeepConnected(),
List.of(ConfigBaseVariable.getBbInterfacesClasses()));
try (final DBAccess daSQL = DBAccess.createInterface(configSQL)) {
final List<Data> allData = daSQL.gets(Data.class, new ReadAllColumn(), new AccessDeletedItems());
final List<AlbumOld> allOldAlbums = daSQL.gets(AlbumOld.class, new ReadAllColumn(), new AccessDeletedItems());
final List<ArtistOld> allOldArtist = daSQL.gets(ArtistOld.class, new ReadAllColumn(), new AccessDeletedItems());
final List<GenderOld> allOldGender = daSQL.gets(GenderOld.class, new ReadAllColumn(), new AccessDeletedItems());
final List<TrackOld> allTOldrack = daSQL.gets(TrackOld.class, new ReadAllColumn(), new AccessDeletedItems());
for (final Data elem : allData) {
daMongo.insert(elem, new DirectData());
}
for (final AlbumOld elem : allOldAlbums) {
final Album tmp = new Album();
tmp.oid = elem.getOid();
tmp.deleted = elem.deleted;
tmp.updatedAt = elem.updatedAt;
tmp.createdAt = elem.createdAt;
tmp.name = elem.name;
tmp.description = elem.description;
tmp.covers = elem.covers;
daMongo.insert(tmp, new DirectData());
}
for (final ArtistOld elem : allOldArtist) {
final Artist tmp = new Artist();
tmp.oid = elem.getOid();
tmp.deleted = elem.deleted;
tmp.updatedAt = elem.updatedAt;
tmp.createdAt = elem.createdAt;
tmp.name = elem.name;
tmp.description = elem.description;
tmp.covers = elem.covers;
tmp.firstName = elem.firstName;
tmp.birth = elem.birth;
tmp.death = elem.death;
daMongo.insert(tmp, new DirectData());
}
for (final GenderOld elem : allOldGender) {
final Gender tmp = new Gender();
tmp.oid = elem.getOid();
tmp.deleted = elem.deleted;
tmp.updatedAt = elem.updatedAt;
tmp.createdAt = elem.createdAt;
tmp.name = elem.name;
tmp.description = elem.description;
tmp.covers = elem.covers;
daMongo.insert(tmp, new DirectData());
}
for (final TrackOld elem : allTOldrack) {
final Track tmp = new Track();
tmp.oid = elem.getOid();
tmp.deleted = elem.deleted;
tmp.updatedAt = elem.updatedAt;
tmp.createdAt = elem.createdAt;
tmp.name = elem.name;
tmp.description = elem.description;
tmp.covers = elem.covers;
tmp.genderId = getElementGender(allOldGender, elem.genderId);
tmp.albumId = getElementAlbum(allOldAlbums, elem.albumId);
tmp.track = elem.track;
tmp.dataId = elem.dataId;
tmp.artists = new ArrayList<>();
for (final Long artistId : elem.artists) {
tmp.artists.add(getElementArtist(allOldArtist, artistId));
}
daMongo.insert(tmp, new DirectData());
}
}
});
}
}

View File

@ -1,10 +1,10 @@
package org.kar.karusic.migration.model;
package org.atriasoft.karusic.migration.model;
import java.util.List;
import java.util.UUID;
import org.bson.types.ObjectId;
import org.kar.archidata.annotation.DataJson;
import org.atriasoft.archidata.annotation.DataJson;
import jakarta.persistence.Id;

View File

@ -1,4 +1,4 @@
package org.kar.karusic.migration.model;
package org.atriasoft.karusic.migration.model;
import java.util.UUID;

View File

@ -1,4 +1,4 @@
package org.kar.karusic.migration.model;
package org.atriasoft.karusic.migration.model;
import java.util.UUID;

View File

@ -0,0 +1,44 @@
package org.atriasoft.karusic.model;
import java.util.Date;
import java.util.List;
import org.atriasoft.archidata.annotation.DataIfNotExists;
import org.atriasoft.archidata.annotation.apiGenerator.ApiAccessLimitation;
import org.atriasoft.archidata.annotation.apiGenerator.ApiGenerationMode;
import org.atriasoft.archidata.annotation.checker.CheckForeignKey;
import org.atriasoft.archidata.annotation.checker.CollectionNotEmpty;
import org.atriasoft.archidata.model.Data;
import org.atriasoft.archidata.model.OIDGenericDataSoftDelete;
import org.bson.types.ObjectId;
import org.hibernate.validator.constraints.UniqueElements;
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.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
@Entity()
@DataIfNotExists
@JsonInclude(JsonInclude.Include.NON_NULL)
@ApiGenerationMode(create = true, update = true)
public class Album extends OIDGenericDataSoftDelete {
@Column(length = 256)
@Size(min = 1, max = 256)
public String name = null;
@Column(length = 0)
@Size(min = 0, max = 8192)
public String description = null;
@Schema(description = "List of Id of the specific covers")
@Nullable
@CollectionNotEmpty
@UniqueElements
@ApiAccessLimitation(readable = true, creatable = false, updatable = false)
public List<@CheckForeignKey(target = Data.class) @NotNull ObjectId> covers = null;
public Date publication;
}

View File

@ -0,0 +1,56 @@
package org.atriasoft.karusic.model;
import java.util.Date;
import java.util.List;
import org.atriasoft.archidata.annotation.DataIfNotExists;
import org.atriasoft.archidata.annotation.apiGenerator.ApiAccessLimitation;
import org.atriasoft.archidata.annotation.apiGenerator.ApiGenerationMode;
import org.atriasoft.archidata.annotation.checker.CheckForeignKey;
import org.atriasoft.archidata.annotation.checker.CollectionNotEmpty;
import org.atriasoft.archidata.model.Data;
import org.atriasoft.archidata.model.OIDGenericDataSoftDelete;
import org.bson.types.ObjectId;
import org.hibernate.validator.constraints.UniqueElements;
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.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
@Entity()
@DataIfNotExists
@JsonInclude(JsonInclude.Include.NON_NULL)
@ApiGenerationMode(create = true, update = true)
public class Artist extends OIDGenericDataSoftDelete {
@Column(length = 256)
@Size(min = 1, max = 256)
public String name = null;
@Column(length = 0)
@Size(min = 0, max = 8192)
public String description = null;
@Schema(description = "List of Id of the specific covers")
@Nullable
@CollectionNotEmpty
@UniqueElements
@ApiAccessLimitation(readable = true, creatable = false, updatable = false)
public List<@CheckForeignKey(target = Data.class) @NotNull ObjectId> covers = null;
@Column(length = 256)
@Size(min = 1, max = 256)
public String firstName = null;
@Column(length = 256)
@Size(min = 1, max = 256)
public String surname = null;
public Date birth = null;
public Date death = null;
@Override
public String toString() {
return "Artist [id=" + this.oid + ", name=" + this.name + ", description=" + this.description + ", covers=" + this.covers + ", firstName=" + this.firstName + ", surname=" + this.surname
+ ", birth=" + this.birth + ", death=" + this.death + "]";
}
}

View File

@ -0,0 +1,65 @@
package org.atriasoft.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 org.atriasoft.archidata.annotation.DataIfNotExists;
import org.atriasoft.archidata.annotation.apiGenerator.ApiAccessLimitation;
import org.atriasoft.archidata.annotation.apiGenerator.ApiGenerationMode;
import org.atriasoft.archidata.annotation.checker.CheckForeignKey;
import org.atriasoft.archidata.annotation.checker.CollectionNotEmpty;
import org.atriasoft.archidata.model.Data;
import org.atriasoft.archidata.model.OIDGenericDataSoftDelete;
import org.bson.types.ObjectId;
import org.hibernate.validator.constraints.UniqueElements;
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.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
@Entity()
@DataIfNotExists
@JsonInclude(JsonInclude.Include.NON_NULL)
@ApiGenerationMode(create = true, update = true)
public class Gender extends OIDGenericDataSoftDelete {
@Column(length = 256)
@Size(min = 1, max = 256)
public String name = null;
@Column(length = 0)
@Size(min = 0, max = 8192)
public String description = null;
@Schema(description = "List of Id of the specific covers")
@Nullable
@CollectionNotEmpty
@UniqueElements
@ApiAccessLimitation(readable = true, creatable = false, updatable = false)
public List<@CheckForeignKey(target = Data.class) @NotNull ObjectId> covers = null;
public Gender() {}
public Gender(final String name) {
this.name = name;
}
public Gender(final ObjectId oid, final String name) {
this.oid = oid;
this.name = name;
}
}

View File

@ -0,0 +1,54 @@
package org.atriasoft.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 org.atriasoft.archidata.annotation.DataIfNotExists;
import org.atriasoft.archidata.annotation.apiGenerator.ApiAccessLimitation;
import org.atriasoft.archidata.annotation.apiGenerator.ApiGenerationMode;
import org.atriasoft.archidata.annotation.checker.CheckForeignKey;
import org.atriasoft.archidata.annotation.checker.CollectionNotEmpty;
import org.atriasoft.archidata.model.Data;
import org.atriasoft.archidata.model.OIDGenericDataSoftDelete;
import org.bson.types.ObjectId;
import org.hibernate.validator.constraints.UniqueElements;
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.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
@Entity()
@DataIfNotExists
@JsonInclude(JsonInclude.Include.NON_NULL)
@ApiGenerationMode(create = true, update = true)
public class Playlist extends OIDGenericDataSoftDelete {
@Column(length = 256)
@Size(min = 1, max = 256)
public String name = null;
@Column(length = 0)
@Size(min = 0, max = 8192)
public String description = null;
@Schema(description = "List of Id of the specific covers")
@Nullable
@CollectionNotEmpty
@UniqueElements
@ApiAccessLimitation(readable = true, creatable = false, updatable = false)
public List<@CheckForeignKey(target = Data.class) @NotNull ObjectId> covers = null;
public List<@CheckForeignKey(target = Track.class) @NotNull ObjectId> tracks = null;
}

View File

@ -1,4 +1,4 @@
package org.kar.karusic.model;
package org.atriasoft.karusic.model;
public enum State {
// User has remove his account

View File

@ -0,0 +1,71 @@
package org.atriasoft.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 org.atriasoft.archidata.annotation.DataIfNotExists;
import org.atriasoft.archidata.annotation.apiGenerator.ApiAccessLimitation;
import org.atriasoft.archidata.annotation.apiGenerator.ApiGenerationMode;
import org.atriasoft.archidata.annotation.checker.CheckForeignKey;
import org.atriasoft.archidata.annotation.checker.CollectionNotEmpty;
import org.atriasoft.archidata.model.Data;
import org.atriasoft.archidata.model.OIDGenericDataSoftDelete;
import org.bson.types.ObjectId;
import org.hibernate.validator.constraints.UniqueElements;
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.validation.constraints.NotNull;
import jakarta.validation.constraints.PositiveOrZero;
import jakarta.validation.constraints.Size;
@Entity()
@DataIfNotExists
@JsonInclude(JsonInclude.Include.NON_NULL)
@ApiGenerationMode(create = true, update = true)
public class Track extends OIDGenericDataSoftDelete {
@Column(length = 256)
@Size(min = 1, max = 256)
public String name = null;
@Column(length = 0)
@Size(min = 0, max = 8192)
public String description = null;
@Schema(description = "List of Id of the specific covers")
@Nullable
@CollectionNotEmpty
@UniqueElements
@ApiAccessLimitation(readable = true, creatable = false, updatable = false)
public List<@CheckForeignKey(target = Data.class) @NotNull ObjectId> covers = null;
@CheckForeignKey(target = Gender.class)
public ObjectId genderId = null;
@CheckForeignKey(target = Album.class)
public ObjectId albumId = null;
@PositiveOrZero
public Long track = null;
@CheckForeignKey(target = Data.class)
public ObjectId dataId = null;
@Column(length = 0)
public List<@CheckForeignKey(target = Artist.class) @NotNull ObjectId> artists = null;
@Override
public String toString() {
return "Track [oid=" + this.oid + ", deleted=" + this.deleted + ", createdAt=" + this.createdAt + ", updatedAt=" + this.updatedAt + ", name=" + this.name + ", description=" + this.description
+ ", covers=" + this.covers + ", genderId=" + this.genderId + ", albumId=" + this.albumId + ", track=" + this.track + ", dataId=" + this.dataId + ", artists=" + this.artists + "]";
}
}

View File

@ -1,7 +1,7 @@
package org.kar.karusic.model;
package org.atriasoft.karusic.model;
import org.kar.archidata.annotation.DataIfNotExists;
import org.kar.archidata.model.User;
import org.atriasoft.archidata.annotation.DataIfNotExists;
import org.atriasoft.archidata.model.User;
import com.fasterxml.jackson.annotation.JsonInclude;

View File

@ -0,0 +1,32 @@
package org.atriasoft.karusic.modelOld;
import java.util.Date;
import java.util.List;
import org.atriasoft.archidata.annotation.DataJson;
import org.atriasoft.archidata.model.GenericDataSoftDelete;
import org.bson.types.ObjectId;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Table;
@Table(name = "album")
public class AlbumOld 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()
public List<ObjectId> covers = null;
public Date publication;
@Column(nullable = true)
private final ObjectId oid = new ObjectId();
public ObjectId getOid() {
return this.oid;
}
}

View File

@ -0,0 +1,34 @@
package org.atriasoft.karusic.modelOld;
import java.util.Date;
import java.util.List;
import org.atriasoft.archidata.annotation.DataJson;
import org.atriasoft.archidata.model.GenericDataSoftDelete;
import org.bson.types.ObjectId;
import jakarta.persistence.Column;
import jakarta.persistence.Table;
@Table(name = "artist")
public class ArtistOld extends GenericDataSoftDelete {
@Column(length = 256)
public String name = null;
@Column(length = 0)
public String description = null;
@DataJson()
public List<ObjectId> covers = null;
@Column(length = 256)
public String firstName = null;
@Column(length = 256)
public String surname = null;
public Date birth = null;
public Date death = null;
@Column(nullable = true)
private final ObjectId oid = new ObjectId();
public ObjectId getOid() {
return this.oid;
}
}

View File

@ -0,0 +1,53 @@
package org.atriasoft.karusic.modelOld;
/*
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 org.atriasoft.archidata.annotation.DataJson;
import org.atriasoft.archidata.annotation.checker.CheckForeignKey;
import org.atriasoft.archidata.model.Data;
import org.atriasoft.archidata.model.GenericDataSoftDelete;
import org.bson.types.ObjectId;
import jakarta.persistence.Column;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
@Table(name = "gender")
public class GenderOld extends GenericDataSoftDelete {
@Column(length = 256)
public String name = null;
@Column(length = 0)
public String description = null;
@DataJson()
public List<@CheckForeignKey(target = Data.class) @NotNull ObjectId> covers = null;
public GenderOld() {}
public GenderOld(final String name) {
this.name = name;
}
public GenderOld(final Long id, final String name) {
this.id = id;
this.name = name;
}
@Column(nullable = true)
private final ObjectId oid = new ObjectId();
public ObjectId getOid() {
return this.oid;
}
}

View File

@ -0,0 +1,48 @@
package org.atriasoft.karusic.modelOld;
/*
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 org.atriasoft.archidata.annotation.DataJson;
import org.atriasoft.archidata.annotation.checker.CheckForeignKey;
import org.atriasoft.archidata.model.Data;
import org.atriasoft.archidata.model.GenericDataSoftDelete;
import org.bson.types.ObjectId;
import jakarta.persistence.Column;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
@Table(name = "track")
public class TrackOld extends GenericDataSoftDelete {
@Column(length = 256)
public String name = null;
@Column(length = 0)
public String description = null;
@DataJson()
public List<@CheckForeignKey(target = Data.class) @NotNull ObjectId> covers = null;
public Long genderId = null;
public Long albumId = null;
public Long track = null;
public ObjectId dataId = null;
@DataJson
public List<Long> artists = null;
@Column(nullable = true)
private final ObjectId oid = new ObjectId();
public ObjectId getOid() {
return this.oid;
}
}

View File

@ -1,4 +1,4 @@
package org.kar.karusic.util;
package org.atriasoft.karusic.util;
public class ConfigVariable {
public static final String BASE_NAME = "ORG_KARUSIC_";

View File

@ -1,74 +0,0 @@
package org.kar.karusic;
import java.util.List;
import java.util.logging.LogManager;
import org.kar.archidata.api.DataResource;
import org.kar.archidata.api.ProxyResource;
import org.kar.archidata.exception.DataAccessException;
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;
import org.kar.karusic.api.Front;
import org.kar.karusic.api.GenderResource;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;
public class WebLauncherLocal extends WebLauncher {
private static final Logger LOGGER = LoggerFactory.getLogger(WebLauncherLocal.class);
private WebLauncherLocal() {}
public static void generateObjects() throws Exception {
LOGGER.info("Generate APIs");
final List<Class<?>> listOfResources = List.of(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 {
// Loop-back of logger JDK logging API to SLF4J
LogManager.getLogManager().reset();
SLF4JBridgeHandler.install();
// Generate the APIs in type-script
generateObjects();
final WebLauncherLocal launcher = new WebLauncherLocal();
launcher.process();
launcher.LOGGER.info("end-configure the server & wait finish process:");
Thread.currentThread().join();
launcher.LOGGER.info("STOP the REST server:");
}
@Override
public void process() throws InterruptedException, DataAccessException {
if (true) {
// for local test:
ConfigBaseVariable.apiAdress = "http://0.0.0.0:19080/karusic/api/";
// ConfigBaseVariable.ssoAdress = "https://atria-soft.org/karso/api/";
ConfigBaseVariable.dbPort = "3906";
ConfigBaseVariable.testMode = "true";
}
try {
super.migrateDB();
} catch (final Exception e) {
e.printStackTrace();
while (true) {
LOGGER.error("============================================================================");
LOGGER.error("== Migration fail ==> waiting intervention of administrator...");
LOGGER.error("============================================================================");
Thread.sleep(60 * 60 * 1000);
}
}
super.process();
}
}

View File

@ -1,101 +0,0 @@
package org.kar.karusic.api;
import java.io.InputStream;
import java.util.List;
import org.bson.types.ObjectId;
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.DBAccess;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.addOnSQL.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;
import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.PATCH;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/artist")
@Produces({ MediaType.APPLICATION_JSON })
public class ArtistResource {
private static final Logger LOGGER = LoggerFactory.getLogger(ArtistResource.class);
static final ArtistChecker CHECKER = new ArtistChecker();
@GET
@Path("{id}")
@RolesAllowed("USER")
public Artist get(@PathParam("id") final Long id) throws Exception {
return DataAccess.get(Artist.class, id);
}
@GET
@RolesAllowed("USER")
public List<Artist> gets() throws Exception {
return DataAccess.gets(Artist.class);
}
@POST
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Artist post(final Artist data) throws Exception {
return DataAccess.insert(data, new CheckFunction(CHECKER));
}
@PATCH
@Path("{id}")
@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, new CheckFunction(CHECKER));
return DataAccess.get(Artist.class, id);
}
@DELETE
@Path("{id}")
@RolesAllowed("ADMIN")
public void remove(@PathParam("id") final Long id) throws Exception {
DataAccess.delete(Artist.class, id);
}
@POST
@Path("{id}/cover")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@TypeScriptProgress
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 {
try (DBAccess db = DBAccess.createInterface()) {
if (uri != null) {
DataTools.uploadCoverFromUri(db, Artist.class, id, uri);
} else {
DataTools.uploadCover(db, Artist.class, id, fileInputStream, fileMetaData);
}
return db.get(Artist.class, id);
}
}
@DELETE
@Path("{id}/cover/{coverId}")
@RolesAllowed("ADMIN")
public Artist removeCover(@PathParam("id") final Long id, @PathParam("coverId") final ObjectId coverId) throws Exception {
try (DBAccess db = DBAccess.createInterface()) {
AddOnDataJson.removeLink(db, Artist.class, "id", id, "covers", coverId);
return db.get(Artist.class, id);
}
}
}

View File

@ -1,101 +0,0 @@
package org.kar.karusic.api;
import java.io.InputStream;
import java.util.List;
import org.bson.types.ObjectId;
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.DBAccess;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.addOnSQL.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;
import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.PATCH;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/gender")
@Produces({ MediaType.APPLICATION_JSON })
public class GenderResource {
private static final Logger LOGGER = LoggerFactory.getLogger(GenderResource.class);
static final GenderChecker CHECKER = new GenderChecker();
@GET
@Path("{id}")
@RolesAllowed("USER")
public Gender get(@PathParam("id") final Long id) throws Exception {
return DataAccess.get(Gender.class, id);
}
@GET
@RolesAllowed("USER")
public List<Gender> gets() throws Exception {
return DataAccess.gets(Gender.class);
}
@POST
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Gender post(final Gender data) throws Exception {
return DataAccess.insert(data, new CheckFunction(CHECKER));
}
@PATCH
@Path("{id}")
@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, new CheckFunction(CHECKER));
return DataAccess.get(Gender.class, id);
}
@DELETE
@Path("{id}")
@RolesAllowed("ADMIN")
public void remove(@PathParam("id") final Long id) throws Exception {
DataAccess.delete(Gender.class, id);
}
@POST
@Path("{id}/cover")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@TypeScriptProgress
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 {
try (DBAccess db = DBAccess.createInterface()) {
if (uri != null) {
DataTools.uploadCoverFromUri(db, Gender.class, id, uri);
} else {
DataTools.uploadCover(db, Gender.class, id, fileInputStream, fileMetaData);
}
return db.get(Gender.class, id);
}
}
@DELETE
@Path("{id}/cover/{coverId}")
@RolesAllowed("ADMIN")
public Gender removeCover(@PathParam("id") final Long id, @PathParam("coverId") final ObjectId coverId) throws Exception {
try (DBAccess db = DBAccess.createInterface()) {
AddOnDataJson.removeLink(db, Gender.class, "id", id, "covers", coverId);
return db.get(Gender.class, id);
}
}
}

View File

@ -1,115 +0,0 @@
package org.kar.karusic.api;
import java.io.InputStream;
import java.util.List;
import org.bson.types.ObjectId;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.kar.archidata.annotation.AsyncType;
import org.kar.archidata.dataAccess.DBAccess;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.addOnSQL.AddOnDataJson;
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;
import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.PATCH;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/playlist")
@Produces({ MediaType.APPLICATION_JSON })
public class PlaylistResource {
private static final Logger LOGGER = LoggerFactory.getLogger(PlaylistResource.class);
static final TrackChecker CHECKER = new TrackChecker();
@GET
@Path("{id}")
@RolesAllowed("USER")
public Playlist get(@PathParam("id") final Long id) throws Exception {
return DataAccess.get(Playlist.class, id);
}
@GET
@RolesAllowed("USER")
public List<Playlist> gets() throws Exception {
return DataAccess.gets(Playlist.class);
}
@POST
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Playlist post(final Playlist data) throws Exception {
return DataAccess.insert(data, new CheckFunction(CHECKER));
}
@PATCH
@Path("{id}")
@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, new CheckFunction(CHECKER));
return DataAccess.get(Playlist.class, id);
}
@DELETE
@Path("{id}")
@RolesAllowed("ADMIN")
public void remove(@PathParam("id") final Long id) throws Exception {
DataAccess.delete(Playlist.class, id);
}
@POST
@Path("{id}/track/{trackId}")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
public Playlist addTrack(@PathParam("id") final Long id, @PathParam("trackId") final Long trackId) throws Exception {
try (DBAccess db = DBAccess.createInterface()) {
AddOnDataJson.removeLink(db, Playlist.class, "id", id, "track", trackId);
return db.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 {
try (DBAccess db = DBAccess.createInterface()) {
AddOnDataJson.removeLink(db, Playlist.class, "id", id, "track", trackId);
return db.get(Playlist.class, id);
}
}
@POST
@Path("{id}/cover")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@AsyncType(Playlist.class)
public void uploadCover(@PathParam("id") final Long id, @FormDataParam("file") final InputStream fileInputStream, @FormDataParam("file") final FormDataContentDisposition fileMetaData)
throws Exception {
try (DBAccess db = DBAccess.createInterface()) {
DataTools.uploadCover(db, Playlist.class, id, fileInputStream, fileMetaData);
}
}
@DELETE
@Path("{id}/cover/{coverId}")
@RolesAllowed("ADMIN")
public Playlist removeCover(@PathParam("id") final Long id, @PathParam("coverId") final ObjectId coverId) throws Exception {
try (DBAccess db = DBAccess.createInterface()) {
AddOnDataJson.removeLink(db, Playlist.class, "id", id, "covers", coverId);
return DataAccess.get(Playlist.class, id);
}
}
}

View File

@ -1,60 +0,0 @@
package org.kar.karusic.internal;
//import io.scenarium.logger.LogLevel;
//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);
// }
}

View File

@ -1,109 +0,0 @@
package org.kar.karusic.migration;
import java.util.List;
import org.kar.archidata.dataAccess.DBAccess;
import org.kar.archidata.migration.MigrationSqlStep;
import org.kar.archidata.model.Data;
import org.kar.archidata.model.User;
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;
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);
@Override
public String getName() {
return "Initialization";
}
public Initialization() {
}
@Override
public void generateStep() throws Exception {
for (final Class<?> elem : CLASSES_BASE) {
addClass(elem);
}
addAction((final DBAccess 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;
""", "mysql");
addAction("""
ALTER TABLE `artist` AUTO_INCREMENT = 1000;
""", "mysql");
addAction("""
ALTER TABLE `gender` AUTO_INCREMENT = 1000;
""", "mysql");
addAction("""
ALTER TABLE `playlist` AUTO_INCREMENT = 1000;
""", "mysql");
addAction("""
ALTER TABLE `track` AUTO_INCREMENT = 1000;
""", "mysql");
addAction("""
ALTER TABLE `user` AUTO_INCREMENT = 1000;
""", "mysql");
}
public static void dropAll(final DBAccess 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 DBAccess 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

@ -1,41 +0,0 @@
package org.kar.karusic.model;
import java.time.LocalDate;
import java.util.List;
import org.bson.types.ObjectId;
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 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<ObjectId> covers = null;
public LocalDate publication = null;
}

View File

@ -1,52 +0,0 @@
package org.kar.karusic.model;
import java.time.LocalDate;
import java.util.List;
import org.bson.types.ObjectId;
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 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<ObjectId> covers = null;
@Column(length = 256)
public String firstName = null;
@Column(length = 256)
public String surname = null;
public LocalDate birth = null;
public LocalDate death = null;
@Override
public String toString() {
return "Artist [id=" + this.id + ", name=" + this.name + ", description=" + this.description + ", covers=" + this.covers + ", firstName=" + this.firstName + ", surname=" + this.surname
+ ", birth=" + this.birth + ", death=" + this.death + "]";
}
}

View File

@ -1,59 +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 org.bson.types.ObjectId;
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 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<ObjectId> covers = null;
public Gender() {}
public Gender(final Long id, final String name) {
this.id = id;
this.name = name;
}
}

View File

@ -1,55 +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 org.bson.types.ObjectId;
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 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<ObjectId> covers = null;
@ManyToMany(fetch = FetchType.LAZY, targetEntity = Track.class)
public List<ObjectId> tracks = null;
}

View File

@ -1,66 +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 org.bson.types.ObjectId;
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 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<ObjectId> covers = null;
public Long genderId = null;
public Long albumId = null;
public Long track = null;
public ObjectId dataId = null;
// @ManyToMany(fetch = FetchType.LAZY, targetEntity = Artist.class)
@DataJson
@Column(length = 0)
public List<Long> artists = null;
@Override
public String toString() {
return "Track [id=" + this.id + ", deleted=" + this.deleted + ", createdAt=" + this.createdAt + ", updatedAt=" + this.updatedAt + ", name=" + this.name + ", description=" + this.description
+ ", covers=" + this.covers + ", genderId=" + this.genderId + ", albumId=" + this.albumId + ", track=" + this.track + ", dataId=" + this.dataId + ", artists=" + this.artists + "]";
}
}

View File

@ -1,18 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%d{HH:mm:ss.SSS} [%thread] %highlight(%-5level) %logger - %msg%n
</pattern>
<!--
<pattern>
%d{HH:mm:ss.SSS} | %thread | %highlight(%-5level) | %logger - %msg%n
</pattern>
-->
</encoder>
</appender>
<root level="info">
<appender-ref ref="CONSOLE" />
</root>
</configuration>
<!-- environment detection (defaut: dev) -->
<property name="LOG_LEVEL_ENV" value="${LOG_LEVEL:-dev}" />
<!-- Appender for development -->
<if condition="property(&quot;LOG_LEVEL_ENV&quot;).equals(&quot;dev&quot;)">
<then>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%green(%d{HH:mm:ss.SSS}) %highlight(%-5level) %-30((%file:%line\)): %msg%n</pattern>
</encoder>
</appender>
<logger name="org.atriasoft.karusic" level="TRACE" />
<logger name="org.atriasoft.archidata" level="DEBUG" />
<root level="INFO">
<appender-ref ref="CONSOLE" />
</root>
</then>
</if>
<!-- Appender for production -->
<if condition="property(&quot;LOG_LEVEL_ENV&quot;).matches(&quot;^prod.*&quot;)">
<then>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%thread] %level %logger - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE" />
</root>
</then>
</if>
<if condition="property(&quot;LOG_LEVEL_ENV&quot;).equals(&quot;prod-debug&quot;)">
<then>
<logger name="org.atriasoft.karusic" level="DEBUG" />
</then>
</if>
<if condition="property(&quot;LOG_LEVEL_ENV&quot;).equals(&quot;prod-trace&quot;)">
<then>
<logger name="org.atriasoft.karusic" level="TRACE" />
<logger name="org.atriasoft.archidata" level="DEBUG" />
</then>
</if>
<if condition="property(&quot;LOG_LEVEL_ENV&quot;).equals(&quot;prod-trace-full&quot;)">
<then>
<logger name="org.atriasoft.karusic" level="TRACE" />
<logger name="org.atriasoft.archidata" level="TRACE" />
</then>
</if>
</configuration>

View File

@ -1,42 +0,0 @@
# SLF4J's SimpleLogger configuration file
# Simple implementation of Logger that sends all enabled log messages, for all defined loggers, to System.err.
# 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=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.
#org.slf4j.simpleLogger.showDateTime=false
# The date and time format to be used in the output messages.
# The pattern describing the date and time format is the same that is used in java.text.SimpleDateFormat.
# If the format is not specified or is invalid, the default format is used.
# The default format is yyyy-MM-dd HH:mm:ss:SSS Z.
#org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS Z
# Set to true if you want to output the current thread name.
# Defaults to true.
org.slf4j.simpleLogger.showThreadName=true
# Set to true if you want the Logger instance name to be included in output messages.
# Defaults to true.
#org.slf4j.simpleLogger.showLogName=true
# Set to true if you want the last component of the name to be included in output messages.
# Defaults to false.
#org.slf4j.simpleLogger.showShortLogName=false
# Utilise les codes ANSI pour la couleur
org.slf4j.simpleLogger.warnColor=\u001B[33m
org.slf4j.simpleLogger.errorColor=\u001B[31m
org.slf4j.simpleLogger.infoColor=\u001B[32m
org.slf4j.simpleLogger.debugColor=\u001B[34m

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,10 +1,11 @@
package test.kar.karusic;
package test.atriasoft.karusic;
import java.util.Map;
import org.kar.archidata.tools.JWTWrapper;
import org.atriasoft.archidata.filter.PartRight;
import org.atriasoft.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)));
static String USER_TOKEN = JWTWrapper.createJwtTestToken(16512, "test_user_login", "KarAuth", "karusic", Map.of("karusic", Map.of("USER", PartRight.READ)));
static String ADMIN_TOKEN = JWTWrapper.createJwtTestToken(16512, "test_admin_login", "KarAuth", "karusic", Map.of("karusic", Map.of("USER", PartRight.READ_WRITE, "ADMIN", PartRight.READ_WRITE)));
}

View File

@ -1,19 +1,12 @@
package test.kar.karusic;
package test.atriasoft.karusic;
import java.io.IOException;
import java.util.List;
import org.kar.archidata.dataAccess.DBAccess;
import org.kar.archidata.db.DbConfig;
import org.kar.archidata.db.DbIoFactory;
import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.kar.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;
import org.kar.karusic.model.UserKarusic;
import org.atriasoft.archidata.dataAccess.DBAccess;
import org.atriasoft.archidata.db.DbConfig;
import org.atriasoft.archidata.db.DbIoFactory;
import org.atriasoft.archidata.exception.DataAccessException;
import org.atriasoft.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -35,14 +28,10 @@ public class ConfigureDb {
if (modeTestForced != null) {
modeTest = modeTestForced;
}
final List<Class<?>> listObject = List.of( //
Album.class, //
Artist.class, //
Gender.class, //
Playlist.class, //
Track.class, //
UserKarusic.class //
);
// for local test:
ConfigBaseVariable.apiAdress = "http://127.0.0.1:12342/test/api/";
// Enable the test mode permit to access to the test token (never use it in production).
ConfigBaseVariable.testMode = "true";
if ("SQLITE-MEMORY".equalsIgnoreCase(modeTest)) {
ConfigBaseVariable.dbType = "sqlite";
ConfigBaseVariable.bdDatabase = null;
@ -55,15 +44,15 @@ public class ConfigureDb {
ConfigBaseVariable.dbKeepConnected = "true";
} else if ("MY-SQL".equalsIgnoreCase(modeTest)) {
ConfigBaseVariable.dbType = "mysql";
ConfigBaseVariable.bdDatabase = "test_karusic_db";
ConfigBaseVariable.bdDatabase = "test.atriasoftusic_db";
ConfigBaseVariable.dbPort = "3906";
ConfigBaseVariable.dbUser = "root";
} else if ("MONGO".equalsIgnoreCase(modeTest)) {
ConfigBaseVariable.dbType = "mongo";
ConfigBaseVariable.bdDatabase = "test_karusic_db";
ConfigBaseVariable.bdDatabase = "test.atriasoftusic_db";
} else {
// User local modification ...
ConfigBaseVariable.bdDatabase = "test_karusic_db";
ConfigBaseVariable.bdDatabase = "test.atriasoftusic_db";
ConfigBaseVariable.dbPort = "3906";
ConfigBaseVariable.dbUser = "root";
}

View File

@ -0,0 +1,15 @@
package test.atriasoft.karusic;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Objects;
public class ResourceUtils {
public static File getResourceFile(final String resourcePath) throws IOException {
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
final URL resource = Objects.requireNonNull(classLoader.getResource(resourcePath), "Fichier non trouvé : " + resourcePath);
return new File(resource.getFile());
}
}

View File

@ -1,4 +1,4 @@
package test.kar.karusic;
package test.atriasoft.karusic;
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
import org.junit.jupiter.api.extension.ExecutionCondition;

View File

@ -1,21 +1,24 @@
package test.kar.karusic;
package test.atriasoft.karusic;
import org.atriasoft.archidata.exception.RESTErrorResponseException;
import org.atriasoft.archidata.tools.ConfigBaseVariable;
import org.atriasoft.archidata.tools.RESTApi;
import org.atriasoft.karusic.api.HealthCheck.HealthResult;
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.tools.ConfigBaseVariable;
import org.kar.archidata.tools.RESTApi;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ExtendWith(StepwiseExtension.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class TestBase {
private final static Logger LOGGER = LoggerFactory.getLogger(TestBase.class);
public final static String ENDPOINT_NAME = "species/";
public class TestHealthCheck {
private final static Logger LOGGER = LoggerFactory.getLogger(TestHealthCheck.class);
static WebLauncherTest webInterface = null;
static RESTApi api = null;
@ -25,8 +28,6 @@ public class TestBase {
ConfigureDb.configure();
LOGGER.info("configure server ...");
webInterface = new WebLauncherTest();
LOGGER.info("Clean previous table");
LOGGER.info("Start REST (BEGIN)");
webInterface.process();
LOGGER.info("Start REST (DONE)");
@ -42,9 +43,17 @@ public class TestBase {
ConfigureDb.clear();
}
@Order(1)
@Test
public static void TestEmpty() throws Exception {
public void checkHealthCheck() throws Exception {
final HealthResult result = api.request("health_check").get().fetch(HealthResult.class);
Assertions.assertEquals(result.value(), "alive and kicking");
}
@Order(2)
@Test
public void checkHealthCheckWrongAPI() throws Exception {
Assertions.assertThrows(RESTErrorResponseException.class, () -> api.request("health_check_kaboom").get().fetch());
}
}

View File

@ -0,0 +1,115 @@
package test.atriasoft.karusic;
import java.io.File;
import java.io.IOException;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
import org.atriasoft.archidata.tools.ConfigBaseVariable;
import org.atriasoft.archidata.tools.RESTApi;
import org.atriasoft.karusic.model.Track;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ExtendWith(StepwiseExtension.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class TestTrack {
private final static Logger LOGGER = LoggerFactory.getLogger(TestTrack.class);
public final static String ENDPOINT_NAME = "track";
public final static String ENDPOINT_DATA_NAME = "data";
static WebLauncherTest webInterface = null;
static RESTApi api = null;
@BeforeAll
public static void configureWebServer() throws Exception {
ConfigureDb.configure();
LOGGER.info("configure server ...");
webInterface = new WebLauncherTest();
LOGGER.info("Create BDD");
webInterface.migrateDB();
LOGGER.info("Start REST (BEGIN)");
webInterface.process();
LOGGER.info("Start REST (DONE)");
api = new RESTApi(ConfigBaseVariable.apiAdress);
api.setToken(Common.ADMIN_TOKEN);
}
@AfterAll
public static void stopWebServer() throws Exception {
LOGGER.info("Kill the web server");
webInterface.stop();
webInterface = null;
ConfigureDb.clear();
}
public static boolean compareResponseWithFile(final byte[] responseBody, final File file) throws IOException {
final byte[] fileBytes = Files.readAllBytes(file.toPath());
if (responseBody == null && fileBytes == null) {
return true;
}
if (responseBody == null || fileBytes == null) {
return false;
}
if (responseBody.length != fileBytes.length) {
LOGGER.error("The data have not the same size: {} != {}(ref)", responseBody.length, fileBytes.length);
return false;
}
for (int iii = 0; iii < responseBody.length; iii++) {
if (responseBody[iii] != fileBytes[iii]) {
LOGGER.error("Detect error at the {}/{} element", iii, responseBody.length);
return false;
}
}
return true;
}
@Order(1)
@Test
public void createTrack() throws Exception {
final File dataToUpload = ResourceUtils.getResourceFile("icon-192x192.png");
final Track data = new Track();
data.name = "test track";
data.description = "My track description";
final Map<String, Object> multipart = new HashMap<>();
multipart.put("title", data.name);
multipart.put("genderId", null);
multipart.put("artistId", null);
multipart.put("albumId", null);
multipart.put("trackId", 9);
multipart.put("file", dataToUpload);
final Track inserted = api.request(TestTrack.ENDPOINT_NAME, "upload").post().bodyMultipart(multipart).fetch(Track.class);
Assertions.assertNotNull(inserted);
Assertions.assertNotNull(inserted.oid);
// Assertions.assertTrue(inserted.oid >= 0);
Assertions.assertNull(inserted.updatedAt);
Assertions.assertNull(inserted.createdAt);
Assertions.assertNull(inserted.deleted);
Assertions.assertNotNull(inserted.name);
Assertions.assertEquals(data.name, inserted.name);
Assertions.assertNotNull(inserted.dataId);
// Retrieve Data:
final HttpResponse<byte[]> retreiveData = api.request(TestTrack.ENDPOINT_DATA_NAME, inserted.dataId.toString()).get().fetchByte();
Assertions.assertNotNull(retreiveData);
Assertions.assertEquals(200, retreiveData.statusCode());
Assertions.assertEquals("11999", retreiveData.headers().firstValue("content-length").orElse(null));
final String contentType = retreiveData.headers().firstValue("content-type").orElse(null);
Assertions.assertEquals("image/png", contentType);
Assertions.assertTrue(compareResponseWithFile(retreiveData.body(), dataToUpload));
}
}

View File

@ -1,7 +1,7 @@
package test.kar.karusic;
package test.atriasoft.karusic;
import org.kar.karusic.WebLauncher;
import org.atriasoft.karusic.WebLauncher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

1
front/.env Normal file
View File

@ -0,0 +1 @@
NODE_ENV=development

View File

@ -4,23 +4,11 @@ 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,
},
};
import { ColorModeProvider } from '../src/components/ui/color-mode';
import { Toaster } from '../src/components/ui/toaster';
import { systemTheme } from '../src/theme/theme';
// .
const DocumentationWrapper = ({ children }) => {
return (
<Box id="start-ui-storybook-wrapper" p="4" pb="8" flex="1">
@ -31,13 +19,16 @@ const DocumentationWrapper = ({ children }) => {
export const decorators = [
(Story, context) => (
<ChakraProvider theme={theme}>
{/* Using MemoryRouter to avoid route clashing with Storybook */}
<MemoryRouter>
<DocumentationWrapper>
<Story {...context} />
</DocumentationWrapper>
</MemoryRouter>
</ChakraProvider>
<ColorModeProvider>
<ChakraProvider value={systemTheme}>
{/* Using MemoryRouter to avoid route clashing with Storybook */}
<MemoryRouter>
<DocumentationWrapper>
<Story {...context} />
</DocumentationWrapper>
</MemoryRouter>
<Toaster />
</ChakraProvider>
</ColorModeProvider>
),
];

View File

@ -1,6 +0,0 @@
{
"display": "2025-01-06",
"version": "0.0.1-dev\n - 2025-01-06T00:49:52+01:00",
"commit": "0.0.1-dev\n",
"date": "2025-01-06T00:49:52+01:00"
}

View File

@ -1,25 +0,0 @@
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();

10637
front/config sample.yaml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="manifest" href="/manifest.json" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Karusic</title>
<link rel="icon" href="/favicon.ico" />

View File

@ -3,16 +3,7 @@ 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/**',
],
ignore: [],
};
export default config;

View File

@ -12,12 +12,13 @@
"node": ">=20"
},
"scripts": {
"update_packages": "ncu --upgrade",
"update_packages": "ncu --target minor",
"upgrade_packages": "ncu --upgrade ",
"install_dependency": "pnpm install",
"test": "vitest run",
"test:watch": "vitest watch",
"build": "tsc && vite build",
"static:build": "node build.js && pnpm build",
"static:build": "pnpm build",
"dev": "vite",
"pretty": "prettier -w .",
"lint": "pnpm tsc --noEmit",
@ -28,84 +29,65 @@
"*.{ts,tsx,js,jsx,json}": "prettier --write"
},
"dependencies": {
"@chakra-ui/anatomy": "2.2.2",
"@chakra-ui/cli": "2.4.1",
"@chakra-ui/react": "2.8.2",
"@chakra-ui/theme-tools": "2.2.6",
"@dnd-kit/core": "6.3.1",
"@dnd-kit/modifiers": "9.0.0",
"@dnd-kit/sortable": "10.0.0",
"@dnd-kit/utilities": "3.2.2",
"react-speech-recognition": "4.0.1",
"regenerator-runtime": "0.14.1",
"@trivago/prettier-plugin-sort-imports": "5.2.2",
"@chakra-ui/cli": "3.17.0",
"@chakra-ui/react": "3.17.0",
"@emotion/react": "11.14.0",
"@emotion/styled": "11.14.0",
"@formiz/core": "2.4.5",
"@formiz/validations": "2.0.1",
"allotment": "1.20.2",
"allotment": "1.20.3",
"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.9.0",
"react-custom-scrollbars": "4.2.1",
"react-day-picker": "9.5.0",
"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.9.0",
"react-simple-keyboard": "3.8.33",
"react-sticky-el": "2.1.1",
"next-themes": "^0.4.6",
"react": "19.1.0",
"react-dom": "19.1.0",
"react-error-boundary": "5.0.0",
"react-icons": "5.5.0",
"react-router-dom": "7.5.3",
"react-select": "5.10.1",
"react-use": "17.6.0",
"react-use-draggable-scroll": "0.4.7",
"react-virtuoso": "4.12.3",
"ts-pattern": "5.6.0",
"uuid": "11.0.4",
"zod": "3.24.1",
"zustand": "5.0.2"
"zod": "3.24.3",
"zustand": "5.0.3"
},
"devDependencies": {
"@chakra-ui/styled-system": "2.12.0",
"@playwright/test": "1.49.1",
"@storybook/addon-actions": "8.4.7",
"@storybook/addon-essentials": "8.4.7",
"@storybook/addon-links": "8.4.7",
"@storybook/addon-mdx-gfm": "8.4.7",
"@storybook/react": "8.4.7",
"@storybook/react-vite": "8.4.7",
"@storybook/theming": "8.4.7",
"@chakra-ui/styled-system": "^2.12.0",
"@playwright/test": "1.52.0",
"@storybook/addon-actions": "8.6.12",
"@storybook/addon-essentials": "8.6.12",
"@storybook/addon-links": "8.6.12",
"@storybook/addon-mdx-gfm": "8.6.12",
"@storybook/react": "8.6.12",
"@storybook/react-vite": "8.6.12",
"@storybook/theming": "8.6.12",
"@testing-library/jest-dom": "6.6.3",
"@testing-library/react": "16.1.0",
"@testing-library/user-event": "14.5.2",
"@trivago/prettier-plugin-sort-imports": "5.2.1",
"@testing-library/react": "16.3.0",
"@testing-library/user-event": "14.6.1",
"@trivago/prettier-plugin-sort-imports": "5.2.2",
"@types/jest": "29.5.14",
"@types/node": "22.10.5",
"@types/react": "18.3.8",
"@types/react-dom": "18.3.0",
"@types/react-sticky-el": "1.0.7",
"@typescript-eslint/eslint-plugin": "8.19.0",
"@typescript-eslint/parser": "8.19.0",
"@vitejs/plugin-react": "4.3.4",
"eslint": "9.17.0",
"eslint-plugin-codeceptjs": "1.3.0",
"@types/node": "22.15.3",
"@types/react": "19.1.2",
"@types/react-dom": "19.1.3",
"@typescript-eslint/eslint-plugin": "8.31.1",
"@typescript-eslint/parser": "8.31.1",
"@vitejs/plugin-react": "4.4.1",
"eslint": "9.25.1",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-react": "7.37.3",
"eslint-plugin-react-hooks": "5.1.0",
"eslint-plugin-storybook": "0.11.2",
"eslint-plugin-react": "7.37.5",
"eslint-plugin-react-hooks": "5.2.0",
"eslint-plugin-storybook": "0.12.0",
"jest": "29.7.0",
"jest-environment-jsdom": "29.7.0",
"knip": "5.41.1",
"lint-staged": "15.3.0",
"npm-check-updates": "^17.1.13",
"prettier": "3.4.2",
"puppeteer": "23.11.1",
"react-is": "19.0.0",
"storybook": "8.4.7",
"knip": "5.52.0",
"lint-staged": "15.5.1",
"npm-check-updates": "^18.0.1",
"prettier": "3.5.3",
"puppeteer": "24.7.2",
"react-is": "19.1.0",
"storybook": "8.6.12",
"ts-node": "10.9.2",
"typescript": "5.7.2",
"vite": "6.0.7",
"vitest": "2.1.8"
"typescript": "5.8.3",
"vite": "6.3.4",
"vitest": "3.1.2"
}
}

7633
front/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

View File

@ -0,0 +1,21 @@
{
"name": "Karusic",
"short_name": "Karusic",
"description": "(K)angaroo (A)nd (R)abbit m(usic) is a music streaming",
"start_url": "/karusic/",
"display": "standalone",
"background_color": "#000000",
"theme_color": "#FFFFFF",
"icons": [
{
"src": "/karusic/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/karusic/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}

View File

@ -1,117 +1,19 @@
import { useState } from 'react';
import { ErrorBoundary } from '@/errors/ErrorBoundary';
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]}`;
}
};
import { AudioPlayer } from './components';
import { EnvDevelopment } from './components/EnvDevelopment/EnvDevelopment';
import { AppRoutes } from './scene/AppRoutes';
import { ServiceContextProvider } from './service/ServiceContext';
export const App = () => {
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>
<ServiceContextProvider>
<EnvDevelopment />
<ErrorBoundary>
<AppRoutes />
</ErrorBoundary>
<AudioPlayer />
</ServiceContextProvider>
);
};

View File

@ -1,72 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
stroke="currentColor"
fill="currentColor"
stroke-width="0"
viewBox="0 0 24 24"
height="250px"
width="250px"
version="1.1"
id="svg2"
sodipodi:docname="404.svg"
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2" />
<sodipodi:namedview
id="namedview2"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="true"
inkscape:zoom="3.448"
inkscape:cx="134.28074"
inkscape:cy="125"
inkscape:window-width="1918"
inkscape:window-height="1044"
inkscape:window-x="0"
inkscape:window-y="17"
inkscape:window-maximized="1"
inkscape:current-layer="svg2">
<inkscape:grid
id="grid2"
units="px"
originx="0"
originy="0"
spacingx="0.096"
spacingy="0.096"
empcolor="#0099e5"
empopacity="0.30196078"
color="#0099e5"
opacity="0.14901961"
empspacing="5"
dotted="false"
gridanglex="30"
gridanglez="30"
visible="true" />
</sodipodi:namedview>
<path
fill="none"
d="M0 0h24v24H0z"
id="path1" />
<path
d="M13 10h5l3-3-3-3h-5V2h-2v2H4v6h7v2H6l-3 3 3 3h5v4h2v-4h7v-6h-7z"
id="path2" />
<path
id="rect2"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.384;stroke-linecap:square"
d="M 17.394219,5.0400499 19.459554,6.9903325 17.438107,9.0722051 4.9259029,9.0569946 4.9284452,5.0338374 Z"
sodipodi:nodetypes="cccccc" />
<path
id="rect2-3"
style="fill:#f8fefb;fill-opacity:1;stroke:none;stroke-width:0.384;stroke-linecap:square"
d="m 6.5757719,13.021525 -2.065335,1.950283 2.021447,2.081873 12.5122061,-0.01521 -0.0025,-4.023157 z"
sodipodi:nodetypes="cccccc" />
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -13,9 +13,9 @@ import {
import { z as zod } from "zod"
import {
Album,
AlbumWrite,
Long,
UUID,
AlbumCreate,
AlbumUpdate,
ObjectId,
ZodAlbum,
isAlbum,
} from "../model";
@ -31,12 +31,12 @@ export namespace AlbumResource {
}: {
restConfig: RESTConfig,
params: {
id: Long,
oid: ObjectId,
},
}): Promise<Album> {
return RESTRequestJson({
restModel: {
endPoint: "/album/{id}",
endPoint: "/album/{oid}",
requestType: HTTPRequestModel.GET,
accept: HTTPMimeType.JSON,
},
@ -75,32 +75,6 @@ export namespace AlbumResource {
restConfig,
}, isGetsTypeReturn);
};
/**
* Update a specific album
*/
export function patch({
restConfig,
params,
data,
}: {
restConfig: RESTConfig,
params: {
id: Long,
},
data: AlbumWrite,
}): Promise<Album> {
return RESTRequestJson({
restModel: {
endPoint: "/album/{id}",
requestType: HTTPRequestModel.PATCH,
contentType: HTTPMimeType.JSON,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
data,
}, isAlbum);
};
/**
* Add an album (when all the data already exist)
*/
@ -109,7 +83,7 @@ export namespace AlbumResource {
data,
}: {
restConfig: RESTConfig,
data: AlbumWrite,
data: AlbumCreate,
}): Promise<Album> {
return RESTRequestJson({
restModel: {
@ -122,6 +96,32 @@ export namespace AlbumResource {
data,
}, isAlbum);
};
/**
* Update a specific album
*/
export function put({
restConfig,
params,
data,
}: {
restConfig: RESTConfig,
params: {
oid: ObjectId,
},
data: AlbumUpdate,
}): Promise<Album> {
return RESTRequestJson({
restModel: {
endPoint: "/album/{oid}",
requestType: HTTPRequestModel.PUT,
contentType: HTTPMimeType.JSON,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
data,
}, isAlbum);
};
/**
* Remove a specific album
*/
@ -131,12 +131,12 @@ export namespace AlbumResource {
}: {
restConfig: RESTConfig,
params: {
id: Long,
oid: ObjectId,
},
}): Promise<void> {
return RESTRequestVoid({
restModel: {
endPoint: "/album/{id}",
endPoint: "/album/{oid}",
requestType: HTTPRequestModel.DELETE,
contentType: HTTPMimeType.TEXT_PLAIN,
},
@ -153,13 +153,13 @@ export namespace AlbumResource {
}: {
restConfig: RESTConfig,
params: {
coverId: UUID,
id: Long,
coverId: ObjectId,
oid: ObjectId,
},
}): Promise<Album> {
return RESTRequestJson({
restModel: {
endPoint: "/album/{id}/cover/{coverId}",
endPoint: "/album/{oid}/cover/{coverId}",
requestType: HTTPRequestModel.DELETE,
contentType: HTTPMimeType.TEXT_PLAIN,
accept: HTTPMimeType.JSON,
@ -179,7 +179,7 @@ export namespace AlbumResource {
}: {
restConfig: RESTConfig,
params: {
id: Long,
oid: ObjectId,
},
data: {
file?: File,
@ -189,7 +189,7 @@ export namespace AlbumResource {
}): Promise<Album> {
return RESTRequestJson({
restModel: {
endPoint: "/album/{id}/cover",
endPoint: "/album/{oid}/cover",
requestType: HTTPRequestModel.POST,
contentType: HTTPMimeType.MULTIPART,
accept: HTTPMimeType.JSON,

View File

@ -13,8 +13,8 @@ import {
import { z as zod } from "zod"
import {
Artist,
ArtistWrite,
Long,
ArtistCreate,
ArtistUpdate,
ObjectId,
ZodArtist,
isArtist,
@ -28,12 +28,12 @@ export namespace ArtistResource {
}: {
restConfig: RESTConfig,
params: {
id: Long,
oid: ObjectId,
},
}): Promise<Artist> {
return RESTRequestJson({
restModel: {
endPoint: "/artist/{id}",
endPoint: "/artist/{oid}",
requestType: HTTPRequestModel.GET,
accept: HTTPMimeType.JSON,
},
@ -69,35 +69,12 @@ export namespace ArtistResource {
restConfig,
}, isGetsTypeReturn);
};
export function patch({
restConfig,
params,
data,
}: {
restConfig: RESTConfig,
params: {
id: Long,
},
data: ArtistWrite,
}): Promise<Artist> {
return RESTRequestJson({
restModel: {
endPoint: "/artist/{id}",
requestType: HTTPRequestModel.PATCH,
contentType: HTTPMimeType.JSON,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
data,
}, isArtist);
};
export function post({
restConfig,
data,
}: {
restConfig: RESTConfig,
data: ArtistWrite,
data: ArtistCreate,
}): Promise<Artist> {
return RESTRequestJson({
restModel: {
@ -110,18 +87,41 @@ export namespace ArtistResource {
data,
}, isArtist);
};
export function put({
restConfig,
params,
data,
}: {
restConfig: RESTConfig,
params: {
oid: ObjectId,
},
data: ArtistUpdate,
}): Promise<Artist> {
return RESTRequestJson({
restModel: {
endPoint: "/artist/{oid}",
requestType: HTTPRequestModel.PUT,
contentType: HTTPMimeType.JSON,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
data,
}, isArtist);
};
export function remove({
restConfig,
params,
}: {
restConfig: RESTConfig,
params: {
id: Long,
oid: ObjectId,
},
}): Promise<void> {
return RESTRequestVoid({
restModel: {
endPoint: "/artist/{id}",
endPoint: "/artist/{oid}",
requestType: HTTPRequestModel.DELETE,
contentType: HTTPMimeType.TEXT_PLAIN,
},
@ -136,12 +136,12 @@ export namespace ArtistResource {
restConfig: RESTConfig,
params: {
coverId: ObjectId,
id: Long,
oid: ObjectId,
},
}): Promise<Artist> {
return RESTRequestJson({
restModel: {
endPoint: "/artist/{id}/cover/{coverId}",
endPoint: "/artist/{oid}/cover/{coverId}",
requestType: HTTPRequestModel.DELETE,
contentType: HTTPMimeType.TEXT_PLAIN,
accept: HTTPMimeType.JSON,
@ -158,7 +158,7 @@ export namespace ArtistResource {
}: {
restConfig: RESTConfig,
params: {
id: Long,
oid: ObjectId,
},
data: {
file?: File,
@ -168,7 +168,7 @@ export namespace ArtistResource {
}): Promise<Artist> {
return RESTRequestJson({
restModel: {
endPoint: "/artist/{id}/cover",
endPoint: "/artist/{oid}/cover",
requestType: HTTPRequestModel.POST,
contentType: HTTPMimeType.MULTIPART,
accept: HTTPMimeType.JSON,

View File

@ -22,7 +22,7 @@ export namespace DataResource {
restConfig,
queries,
params,
data,
headers,
}: {
restConfig: RESTConfig,
queries: {
@ -32,7 +32,9 @@ export namespace DataResource {
name: string,
oid: ObjectId,
},
data: string,
headers?: {
Range?: string,
},
}): Promise<object> {
return RESTRequestJson({
restModel: {
@ -42,7 +44,7 @@ export namespace DataResource {
restConfig,
params,
queries,
data,
headers,
});
};
/**
@ -52,7 +54,7 @@ export namespace DataResource {
restConfig,
queries,
params,
data,
headers,
}: {
restConfig: RESTConfig,
queries: {
@ -61,7 +63,9 @@ export namespace DataResource {
params: {
oid: ObjectId,
},
data: string,
headers?: {
Range: string,
},
}): Promise<object> {
return RESTRequestJson({
restModel: {
@ -71,7 +75,7 @@ export namespace DataResource {
restConfig,
params,
queries,
data,
headers,
});
};
/**
@ -81,7 +85,7 @@ export namespace DataResource {
restConfig,
queries,
params,
data,
headers,
}: {
restConfig: RESTConfig,
queries: {
@ -90,7 +94,9 @@ export namespace DataResource {
params: {
oid: ObjectId,
},
data: string,
headers?: {
Range: string,
},
}): Promise<object> {
return RESTRequestJson({
restModel: {
@ -100,7 +106,7 @@ export namespace DataResource {
restConfig,
params,
queries,
data,
headers,
});
};
/**

View File

@ -13,8 +13,8 @@ import {
import { z as zod } from "zod"
import {
Gender,
GenderWrite,
Long,
GenderCreate,
GenderUpdate,
ObjectId,
ZodGender,
isGender,
@ -28,12 +28,12 @@ export namespace GenderResource {
}: {
restConfig: RESTConfig,
params: {
id: Long,
oid: ObjectId,
},
}): Promise<Gender> {
return RESTRequestJson({
restModel: {
endPoint: "/gender/{id}",
endPoint: "/gender/{oid}",
requestType: HTTPRequestModel.GET,
accept: HTTPMimeType.JSON,
},
@ -76,14 +76,14 @@ export namespace GenderResource {
}: {
restConfig: RESTConfig,
params: {
id: Long,
oid: ObjectId,
},
data: GenderWrite,
data: GenderUpdate,
}): Promise<Gender> {
return RESTRequestJson({
restModel: {
endPoint: "/gender/{id}",
requestType: HTTPRequestModel.PATCH,
endPoint: "/gender/{oid}",
requestType: HTTPRequestModel.PUT,
contentType: HTTPMimeType.JSON,
accept: HTTPMimeType.JSON,
},
@ -97,7 +97,7 @@ export namespace GenderResource {
data,
}: {
restConfig: RESTConfig,
data: GenderWrite,
data: GenderCreate,
}): Promise<Gender> {
return RESTRequestJson({
restModel: {
@ -116,12 +116,12 @@ export namespace GenderResource {
}: {
restConfig: RESTConfig,
params: {
id: Long,
oid: ObjectId,
},
}): Promise<void> {
return RESTRequestVoid({
restModel: {
endPoint: "/gender/{id}",
endPoint: "/gender/{oid}",
requestType: HTTPRequestModel.DELETE,
contentType: HTTPMimeType.TEXT_PLAIN,
},
@ -136,12 +136,12 @@ export namespace GenderResource {
restConfig: RESTConfig,
params: {
coverId: ObjectId,
id: Long,
oid: ObjectId,
},
}): Promise<Gender> {
return RESTRequestJson({
restModel: {
endPoint: "/gender/{id}/cover/{coverId}",
endPoint: "/gender/{oid}/cover/{coverId}",
requestType: HTTPRequestModel.DELETE,
contentType: HTTPMimeType.TEXT_PLAIN,
accept: HTTPMimeType.JSON,
@ -158,7 +158,7 @@ export namespace GenderResource {
}: {
restConfig: RESTConfig,
params: {
id: Long,
oid: ObjectId,
},
data: {
file?: File,
@ -168,7 +168,7 @@ export namespace GenderResource {
}): Promise<Gender> {
return RESTRequestJson({
restModel: {
endPoint: "/gender/{id}/cover",
endPoint: "/gender/{oid}/cover",
requestType: HTTPRequestModel.POST,
contentType: HTTPMimeType.MULTIPART,
accept: HTTPMimeType.JSON,

View File

@ -11,44 +11,23 @@ import {
import { z as zod } from "zod"
import {
Long,
ObjectId,
Playlist,
PlaylistWrite,
PlaylistCreate,
PlaylistUpdate,
ZodPlaylist,
isPlaylist,
} from "../model";
export namespace PlaylistResource {
export function addTrack({
restConfig,
params,
}: {
restConfig: RESTConfig,
params: {
trackId: Long,
id: Long,
},
}): Promise<Playlist> {
return RESTRequestJson({
restModel: {
endPoint: "/playlist/{id}/track/{trackId}",
requestType: HTTPRequestModel.POST,
contentType: HTTPMimeType.MULTIPART,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
}, isPlaylist);
};
export function get({
restConfig,
params,
}: {
restConfig: RESTConfig,
params: {
id: Long,
id: ObjectId,
},
}): Promise<Playlist> {
return RESTRequestJson({
@ -89,35 +68,12 @@ export namespace PlaylistResource {
restConfig,
}, isGetsTypeReturn);
};
export function patch({
restConfig,
params,
data,
}: {
restConfig: RESTConfig,
params: {
id: Long,
},
data: PlaylistWrite,
}): Promise<Playlist> {
return RESTRequestJson({
restModel: {
endPoint: "/playlist/{id}",
requestType: HTTPRequestModel.PATCH,
contentType: HTTPMimeType.JSON,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
data,
}, isPlaylist);
};
export function post({
restConfig,
data,
}: {
restConfig: RESTConfig,
data: PlaylistWrite,
data: PlaylistCreate,
}): Promise<Playlist> {
return RESTRequestJson({
restModel: {
@ -130,18 +86,41 @@ export namespace PlaylistResource {
data,
}, isPlaylist);
};
export function put({
restConfig,
params,
data,
}: {
restConfig: RESTConfig,
params: {
oid: ObjectId,
},
data: PlaylistUpdate,
}): Promise<Playlist> {
return RESTRequestJson({
restModel: {
endPoint: "/playlist/{oid}",
requestType: HTTPRequestModel.PUT,
contentType: HTTPMimeType.JSON,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
data,
}, isPlaylist);
};
export function remove({
restConfig,
params,
}: {
restConfig: RESTConfig,
params: {
id: Long,
oid: ObjectId,
},
}): Promise<void> {
return RESTRequestVoid({
restModel: {
endPoint: "/playlist/{id}",
endPoint: "/playlist/{oid}",
requestType: HTTPRequestModel.DELETE,
contentType: HTTPMimeType.TEXT_PLAIN,
},
@ -156,33 +135,12 @@ export namespace PlaylistResource {
restConfig: RESTConfig,
params: {
coverId: ObjectId,
id: Long,
oid: ObjectId,
},
}): Promise<Playlist> {
return RESTRequestJson({
restModel: {
endPoint: "/playlist/{id}/cover/{coverId}",
requestType: HTTPRequestModel.DELETE,
contentType: HTTPMimeType.TEXT_PLAIN,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
}, isPlaylist);
};
export function removeTrack({
restConfig,
params,
}: {
restConfig: RESTConfig,
params: {
trackId: Long,
id: Long,
},
}): Promise<Playlist> {
return RESTRequestJson({
restModel: {
endPoint: "/playlist/{id}/track/{trackId}",
endPoint: "/playlist/{oid}/cover/{coverId}",
requestType: HTTPRequestModel.DELETE,
contentType: HTTPMimeType.TEXT_PLAIN,
accept: HTTPMimeType.JSON,
@ -198,7 +156,7 @@ export namespace PlaylistResource {
}: {
restConfig: RESTConfig,
params: {
id: Long,
oid: ObjectId,
},
data: {
file: File,
@ -206,7 +164,7 @@ export namespace PlaylistResource {
}): Promise<Playlist> {
return RESTRequestJson({
restModel: {
endPoint: "/playlist/{id}/cover",
endPoint: "/playlist/{oid}/cover",
requestType: HTTPRequestModel.POST,
contentType: HTTPMimeType.MULTIPART,
accept: HTTPMimeType.JSON,

View File

@ -15,46 +15,26 @@ import {
Long,
ObjectId,
Track,
TrackWrite,
TrackCreate,
TrackUpdate,
ZodTrack,
isTrack,
} from "../model";
export namespace TrackResource {
export function addTrack({
restConfig,
params,
}: {
restConfig: RESTConfig,
params: {
artistId: Long,
id: Long,
},
}): Promise<Track> {
return RESTRequestJson({
restModel: {
endPoint: "/track/{id}/artist/{artistId}",
requestType: HTTPRequestModel.POST,
contentType: HTTPMimeType.MULTIPART,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
}, isTrack);
};
export function get({
restConfig,
params,
}: {
restConfig: RESTConfig,
params: {
id: Long,
oid: ObjectId,
},
}): Promise<Track> {
return RESTRequestJson({
restModel: {
endPoint: "/track/{id}",
endPoint: "/track/{oid}",
requestType: HTTPRequestModel.GET,
accept: HTTPMimeType.JSON,
},
@ -90,35 +70,12 @@ export namespace TrackResource {
restConfig,
}, isGetsTypeReturn);
};
export function patch({
restConfig,
params,
data,
}: {
restConfig: RESTConfig,
params: {
id: Long,
},
data: TrackWrite,
}): Promise<Track> {
return RESTRequestJson({
restModel: {
endPoint: "/track/{id}",
requestType: HTTPRequestModel.PATCH,
contentType: HTTPMimeType.JSON,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
data,
}, isTrack);
};
export function post({
restConfig,
data,
}: {
restConfig: RESTConfig,
data: TrackWrite,
data: TrackCreate,
}): Promise<Track> {
return RESTRequestJson({
restModel: {
@ -131,18 +88,41 @@ export namespace TrackResource {
data,
}, isTrack);
};
export function put({
restConfig,
params,
data,
}: {
restConfig: RESTConfig,
params: {
oid: ObjectId,
},
data: TrackUpdate,
}): Promise<Track> {
return RESTRequestJson({
restModel: {
endPoint: "/track/{oid}",
requestType: HTTPRequestModel.PUT,
contentType: HTTPMimeType.JSON,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
data,
}, isTrack);
};
export function remove({
restConfig,
params,
}: {
restConfig: RESTConfig,
params: {
id: Long,
oid: ObjectId,
},
}): Promise<void> {
return RESTRequestVoid({
restModel: {
endPoint: "/track/{id}",
endPoint: "/track/{oid}",
requestType: HTTPRequestModel.DELETE,
contentType: HTTPMimeType.TEXT_PLAIN,
},
@ -157,33 +137,12 @@ export namespace TrackResource {
restConfig: RESTConfig,
params: {
coverId: ObjectId,
id: Long,
oid: ObjectId,
},
}): Promise<Track> {
return RESTRequestJson({
restModel: {
endPoint: "/track/{id}/cover/{coverId}",
requestType: HTTPRequestModel.DELETE,
contentType: HTTPMimeType.TEXT_PLAIN,
accept: HTTPMimeType.JSON,
},
restConfig,
params,
}, isTrack);
};
export function removeTrack({
restConfig,
params,
}: {
restConfig: RESTConfig,
params: {
artistId: Long,
id: Long,
},
}): Promise<Track> {
return RESTRequestJson({
restModel: {
endPoint: "/track/{id}/artist/{trackId}",
endPoint: "/track/{oid}/cover/{coverId}",
requestType: HTTPRequestModel.DELETE,
contentType: HTTPMimeType.TEXT_PLAIN,
accept: HTTPMimeType.JSON,
@ -200,7 +159,7 @@ export namespace TrackResource {
}: {
restConfig: RESTConfig,
params: {
id: Long,
oid: ObjectId,
},
data: {
file: File,
@ -210,7 +169,7 @@ export namespace TrackResource {
}): Promise<Track> {
return RESTRequestJson({
restModel: {
endPoint: "/track/{id}/cover",
endPoint: "/track/{oid}/cover",
requestType: HTTPRequestModel.POST,
contentType: HTTPMimeType.MULTIPART,
accept: HTTPMimeType.JSON,
@ -230,9 +189,9 @@ export namespace TrackResource {
data: {
file: File,
trackId?: Long,
genderId?: Long,
albumId?: Long,
artistId?: Long,
genderId?: ObjectId,
albumId?: ObjectId,
artistId?: ObjectId,
title: string,
},
callbacks?: RESTCallbacks,

View File

@ -3,18 +3,18 @@
*/
import { z as zod } from "zod";
import {ZodIsoDate} from "./iso-date";
import {ZodObjectId} from "./object-id";
import {ZodLocalDate} from "./local-date";
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteWrite } from "./generic-data-soft-delete";
import {ZodOIDGenericDataSoftDelete, ZodOIDGenericDataSoftDeleteUpdate , ZodOIDGenericDataSoftDeleteCreate } from "./oid-generic-data-soft-delete";
export const ZodAlbum = ZodGenericDataSoftDelete.extend({
name: zod.string().max(256).optional(),
description: zod.string().optional(),
export const ZodAlbum = ZodOIDGenericDataSoftDelete.extend({
name: zod.string().min(1).max(256).optional(),
description: zod.string().max(8192).optional(),
/**
* List of Id of the specific covers
*/
covers: zod.array(ZodObjectId).optional(),
publication: ZodLocalDate.optional(),
covers: zod.array(ZodObjectId).readonly().optional(),
publication: ZodIsoDate.optional(),
});
@ -29,25 +29,39 @@ export function isAlbum(data: any): data is Album {
return false;
}
}
export const ZodAlbumWrite = ZodGenericDataSoftDeleteWrite.extend({
name: zod.string().max(256).nullable().optional(),
description: zod.string().nullable().optional(),
/**
* List of Id of the specific covers
*/
covers: zod.array(ZodObjectId).nullable().optional(),
publication: ZodLocalDate.nullable().optional(),
export const ZodAlbumUpdate = ZodOIDGenericDataSoftDeleteUpdate.extend({
name: zod.string().min(1).max(256).nullable().optional(),
description: zod.string().max(8192).nullable().optional(),
publication: ZodIsoDate.nullable().optional(),
});
export type AlbumWrite = zod.infer<typeof ZodAlbumWrite>;
export type AlbumUpdate = zod.infer<typeof ZodAlbumUpdate>;
export function isAlbumWrite(data: any): data is AlbumWrite {
export function isAlbumUpdate(data: any): data is AlbumUpdate {
try {
ZodAlbumWrite.parse(data);
ZodAlbumUpdate.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodAlbumWrite' error=${e}`);
console.log(`Fail to parse data type='ZodAlbumUpdate' error=${e}`);
return false;
}
}
export const ZodAlbumCreate = ZodOIDGenericDataSoftDeleteCreate.extend({
name: zod.string().min(1).max(256).nullable().optional(),
description: zod.string().max(8192).nullable().optional(),
publication: ZodIsoDate.nullable().optional(),
});
export type AlbumCreate = zod.infer<typeof ZodAlbumCreate>;
export function isAlbumCreate(data: any): data is AlbumCreate {
try {
ZodAlbumCreate.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodAlbumCreate' error=${e}`);
return false;
}
}

View File

@ -3,21 +3,21 @@
*/
import { z as zod } from "zod";
import {ZodIsoDate} from "./iso-date";
import {ZodObjectId} from "./object-id";
import {ZodLocalDate} from "./local-date";
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteWrite } from "./generic-data-soft-delete";
import {ZodOIDGenericDataSoftDelete, ZodOIDGenericDataSoftDeleteUpdate , ZodOIDGenericDataSoftDeleteCreate } from "./oid-generic-data-soft-delete";
export const ZodArtist = ZodGenericDataSoftDelete.extend({
name: zod.string().max(256).optional(),
description: zod.string().optional(),
export const ZodArtist = ZodOIDGenericDataSoftDelete.extend({
name: zod.string().min(1).max(256).optional(),
description: zod.string().max(8192).optional(),
/**
* List of Id of the specific covers
*/
covers: zod.array(ZodObjectId).optional(),
firstName: zod.string().max(256).optional(),
surname: zod.string().max(256).optional(),
birth: ZodLocalDate.optional(),
death: ZodLocalDate.optional(),
covers: zod.array(ZodObjectId).readonly().optional(),
firstName: zod.string().min(1).max(256).optional(),
surname: zod.string().min(1).max(256).optional(),
birth: ZodIsoDate.optional(),
death: ZodIsoDate.optional(),
});
@ -32,28 +32,45 @@ export function isArtist(data: any): data is Artist {
return false;
}
}
export const ZodArtistWrite = ZodGenericDataSoftDeleteWrite.extend({
name: zod.string().max(256).nullable().optional(),
description: zod.string().nullable().optional(),
/**
* List of Id of the specific covers
*/
covers: zod.array(ZodObjectId).nullable().optional(),
firstName: zod.string().max(256).nullable().optional(),
surname: zod.string().max(256).nullable().optional(),
birth: ZodLocalDate.nullable().optional(),
death: ZodLocalDate.nullable().optional(),
export const ZodArtistUpdate = ZodOIDGenericDataSoftDeleteUpdate.extend({
name: zod.string().min(1).max(256).nullable().optional(),
description: zod.string().max(8192).nullable().optional(),
firstName: zod.string().min(1).max(256).nullable().optional(),
surname: zod.string().min(1).max(256).nullable().optional(),
birth: ZodIsoDate.nullable().optional(),
death: ZodIsoDate.nullable().optional(),
});
export type ArtistWrite = zod.infer<typeof ZodArtistWrite>;
export type ArtistUpdate = zod.infer<typeof ZodArtistUpdate>;
export function isArtistWrite(data: any): data is ArtistWrite {
export function isArtistUpdate(data: any): data is ArtistUpdate {
try {
ZodArtistWrite.parse(data);
ZodArtistUpdate.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodArtistWrite' error=${e}`);
console.log(`Fail to parse data type='ZodArtistUpdate' error=${e}`);
return false;
}
}
export const ZodArtistCreate = ZodOIDGenericDataSoftDeleteCreate.extend({
name: zod.string().min(1).max(256).nullable().optional(),
description: zod.string().max(8192).nullable().optional(),
firstName: zod.string().min(1).max(256).nullable().optional(),
surname: zod.string().min(1).max(256).nullable().optional(),
birth: ZodIsoDate.nullable().optional(),
death: ZodIsoDate.nullable().optional(),
});
export type ArtistCreate = zod.infer<typeof ZodArtistCreate>;
export function isArtistCreate(data: any): data is ArtistCreate {
try {
ZodArtistCreate.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodArtistCreate' error=${e}`);
return false;
}
}

View File

@ -4,15 +4,15 @@
import { z as zod } from "zod";
import {ZodObjectId} from "./object-id";
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteWrite } from "./generic-data-soft-delete";
import {ZodOIDGenericDataSoftDelete, ZodOIDGenericDataSoftDeleteUpdate , ZodOIDGenericDataSoftDeleteCreate } from "./oid-generic-data-soft-delete";
export const ZodGender = ZodGenericDataSoftDelete.extend({
name: zod.string().max(256).optional(),
description: zod.string().optional(),
export const ZodGender = ZodOIDGenericDataSoftDelete.extend({
name: zod.string().min(1).max(256).optional(),
description: zod.string().max(8192).optional(),
/**
* List of Id of the specific covers
*/
covers: zod.array(ZodObjectId).optional(),
covers: zod.array(ZodObjectId).readonly().optional(),
});
@ -27,24 +27,37 @@ export function isGender(data: any): data is Gender {
return false;
}
}
export const ZodGenderWrite = ZodGenericDataSoftDeleteWrite.extend({
name: zod.string().max(256).nullable().optional(),
description: zod.string().nullable().optional(),
/**
* List of Id of the specific covers
*/
covers: zod.array(ZodObjectId).nullable().optional(),
export const ZodGenderUpdate = ZodOIDGenericDataSoftDeleteUpdate.extend({
name: zod.string().min(1).max(256).nullable().optional(),
description: zod.string().max(8192).nullable().optional(),
});
export type GenderWrite = zod.infer<typeof ZodGenderWrite>;
export type GenderUpdate = zod.infer<typeof ZodGenderUpdate>;
export function isGenderWrite(data: any): data is GenderWrite {
export function isGenderUpdate(data: any): data is GenderUpdate {
try {
ZodGenderWrite.parse(data);
ZodGenderUpdate.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodGenderWrite' error=${e}`);
console.log(`Fail to parse data type='ZodGenderUpdate' error=${e}`);
return false;
}
}
export const ZodGenderCreate = ZodOIDGenericDataSoftDeleteCreate.extend({
name: zod.string().min(1).max(256).nullable().optional(),
description: zod.string().max(8192).nullable().optional(),
});
export type GenderCreate = zod.infer<typeof ZodGenderCreate>;
export function isGenderCreate(data: any): data is GenderCreate {
try {
ZodGenderCreate.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodGenderCreate' error=${e}`);
return false;
}
}

View File

@ -3,7 +3,7 @@
*/
import { z as zod } from "zod";
import {ZodGenericData, ZodGenericDataWrite } from "./generic-data";
import {ZodGenericData, ZodGenericDataUpdate , ZodGenericDataCreate } from "./generic-data";
export const ZodGenericDataSoftDelete = ZodGenericData.extend({
/**
@ -24,18 +24,29 @@ export function isGenericDataSoftDelete(data: any): data is GenericDataSoftDelet
return false;
}
}
export const ZodGenericDataSoftDeleteWrite = ZodGenericDataWrite.extend({
export const ZodGenericDataSoftDeleteUpdate = ZodGenericDataUpdate;
});
export type GenericDataSoftDeleteUpdate = zod.infer<typeof ZodGenericDataSoftDeleteUpdate>;
export type GenericDataSoftDeleteWrite = zod.infer<typeof ZodGenericDataSoftDeleteWrite>;
export function isGenericDataSoftDeleteWrite(data: any): data is GenericDataSoftDeleteWrite {
export function isGenericDataSoftDeleteUpdate(data: any): data is GenericDataSoftDeleteUpdate {
try {
ZodGenericDataSoftDeleteWrite.parse(data);
ZodGenericDataSoftDeleteUpdate.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodGenericDataSoftDeleteWrite' error=${e}`);
console.log(`Fail to parse data type='ZodGenericDataSoftDeleteUpdate' error=${e}`);
return false;
}
}
export const ZodGenericDataSoftDeleteCreate = ZodGenericDataCreate;
export type GenericDataSoftDeleteCreate = zod.infer<typeof ZodGenericDataSoftDeleteCreate>;
export function isGenericDataSoftDeleteCreate(data: any): data is GenericDataSoftDeleteCreate {
try {
ZodGenericDataSoftDeleteCreate.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodGenericDataSoftDeleteCreate' error=${e}`);
return false;
}
}

View File

@ -3,8 +3,8 @@
*/
import { z as zod } from "zod";
import {ZodGenericTiming, ZodGenericTimingUpdate , ZodGenericTimingCreate } from "./generic-timing";
import {ZodLong} from "./long";
import {ZodGenericTiming, ZodGenericTimingWrite } from "./generic-timing";
export const ZodGenericData = ZodGenericTiming.extend({
/**
@ -25,18 +25,29 @@ export function isGenericData(data: any): data is GenericData {
return false;
}
}
export const ZodGenericDataWrite = ZodGenericTimingWrite.extend({
export const ZodGenericDataUpdate = ZodGenericTimingUpdate;
});
export type GenericDataUpdate = zod.infer<typeof ZodGenericDataUpdate>;
export type GenericDataWrite = zod.infer<typeof ZodGenericDataWrite>;
export function isGenericDataWrite(data: any): data is GenericDataWrite {
export function isGenericDataUpdate(data: any): data is GenericDataUpdate {
try {
ZodGenericDataWrite.parse(data);
ZodGenericDataUpdate.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodGenericDataWrite' error=${e}`);
console.log(`Fail to parse data type='ZodGenericDataUpdate' error=${e}`);
return false;
}
}
export const ZodGenericDataCreate = ZodGenericTimingCreate;
export type GenericDataCreate = zod.infer<typeof ZodGenericDataCreate>;
export function isGenericDataCreate(data: any): data is GenericDataCreate {
try {
ZodGenericDataCreate.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodGenericDataCreate' error=${e}`);
return false;
}
}

View File

@ -28,18 +28,33 @@ export function isGenericTiming(data: any): data is GenericTiming {
return false;
}
}
export const ZodGenericTimingWrite = zod.object({
export const ZodGenericTimingUpdate = zod.object({
});
export type GenericTimingWrite = zod.infer<typeof ZodGenericTimingWrite>;
export type GenericTimingUpdate = zod.infer<typeof ZodGenericTimingUpdate>;
export function isGenericTimingWrite(data: any): data is GenericTimingWrite {
export function isGenericTimingUpdate(data: any): data is GenericTimingUpdate {
try {
ZodGenericTimingWrite.parse(data);
ZodGenericTimingUpdate.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodGenericTimingWrite' error=${e}`);
console.log(`Fail to parse data type='ZodGenericTimingUpdate' error=${e}`);
return false;
}
}
export const ZodGenericTimingCreate = zod.object({
});
export type GenericTimingCreate = zod.infer<typeof ZodGenericTimingCreate>;
export function isGenericTimingCreate(data: any): data is GenericTimingCreate {
try {
ZodGenericTimingCreate.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodGenericTimingCreate' error=${e}`);
return false;
}
}

View File

@ -19,18 +19,3 @@ export function isHealthResult(data: any): data is HealthResult {
return false;
}
}
export const ZodHealthResultWrite = zod.object({
});
export type HealthResultWrite = zod.infer<typeof ZodHealthResultWrite>;
export function isHealthResultWrite(data: any): data is HealthResultWrite {
try {
ZodHealthResultWrite.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodHealthResultWrite' error=${e}`);
return false;
}
}

View File

@ -10,15 +10,20 @@ export * from "./generic-timing"
export * from "./health-result"
export * from "./integer"
export * from "./iso-date"
export * from "./local-date"
export * from "./jwt-header"
export * from "./jwt-payload"
export * from "./jwt-token"
export * from "./long"
export * from "./object-id"
export * from "./oid-generic-data"
export * from "./oid-generic-data-soft-delete"
export * from "./part-right"
export * from "./part-right"
export * from "./playlist"
export * from "./rest-error-response"
export * from "./rest-input-error"
export * from "./timestamp"
export * from "./track"
export * from "./user"
export * from "./user-karusic"
export * from "./user-me"
export * from "./uuid"

View File

@ -0,0 +1,23 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
export const ZodJwtHeader = zod.object({
typ: zod.string().max(128),
alg: zod.string().max(128),
});
export type JwtHeader = zod.infer<typeof ZodJwtHeader>;
export function isJwtHeader(data: any): data is JwtHeader {
try {
ZodJwtHeader.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodJwtHeader' error=${e}`);
return false;
}
}

View File

@ -0,0 +1,29 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
import {ZodLong} from "./long";
export const ZodJwtPayload = zod.object({
sub: zod.string(),
application: zod.string(),
iss: zod.string(),
right: zod.record(zod.string(), zod.record(zod.string(), ZodLong)),
login: zod.string(),
exp: ZodLong,
iat: ZodLong,
});
export type JwtPayload = zod.infer<typeof ZodJwtPayload>;
export function isJwtPayload(data: any): data is JwtPayload {
try {
ZodJwtPayload.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodJwtPayload' error=${e}`);
return false;
}
}

View File

@ -0,0 +1,26 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
import {ZodJwtHeader} from "./jwt-header";
import {ZodJwtPayload} from "./jwt-payload";
export const ZodJwtToken = zod.object({
header: ZodJwtHeader,
payload: ZodJwtPayload,
signature: zod.string(),
});
export type JwtToken = zod.infer<typeof ZodJwtToken>;
export function isJwtToken(data: any): data is JwtToken {
try {
ZodJwtToken.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodJwtToken' error=${e}`);
return false;
}
}

View File

@ -0,0 +1,52 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
import {ZodOIDGenericData, ZodOIDGenericDataUpdate , ZodOIDGenericDataCreate } from "./oid-generic-data";
export const ZodOIDGenericDataSoftDelete = ZodOIDGenericData.extend({
/**
* Deleted state
*/
deleted: zod.boolean().readonly().optional(),
});
export type OIDGenericDataSoftDelete = zod.infer<typeof ZodOIDGenericDataSoftDelete>;
export function isOIDGenericDataSoftDelete(data: any): data is OIDGenericDataSoftDelete {
try {
ZodOIDGenericDataSoftDelete.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodOIDGenericDataSoftDelete' error=${e}`);
return false;
}
}
export const ZodOIDGenericDataSoftDeleteUpdate = ZodOIDGenericDataUpdate;
export type OIDGenericDataSoftDeleteUpdate = zod.infer<typeof ZodOIDGenericDataSoftDeleteUpdate>;
export function isOIDGenericDataSoftDeleteUpdate(data: any): data is OIDGenericDataSoftDeleteUpdate {
try {
ZodOIDGenericDataSoftDeleteUpdate.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodOIDGenericDataSoftDeleteUpdate' error=${e}`);
return false;
}
}
export const ZodOIDGenericDataSoftDeleteCreate = ZodOIDGenericDataCreate;
export type OIDGenericDataSoftDeleteCreate = zod.infer<typeof ZodOIDGenericDataSoftDeleteCreate>;
export function isOIDGenericDataSoftDeleteCreate(data: any): data is OIDGenericDataSoftDeleteCreate {
try {
ZodOIDGenericDataSoftDeleteCreate.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodOIDGenericDataSoftDeleteCreate' error=${e}`);
return false;
}
}

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