77 Commits

Author SHA1 Message Date
8f026d47e1 [RELEASE] Release v1.0.4 2025-01-06 23:48:46 +01:00
de17fc228d [FIX] migration file error 2025-01-06 23:48:21 +01:00
3434a48573 [VERSION] update dev tag version 2025-01-06 23:30:02 +01:00
8f1934e323 [RELEASE] Release v1.0.2 2025-01-06 23:29:13 +01:00
1631d1cab9 [FIX] logger dependency error 2025-01-06 23:24:45 +01:00
b71ea1bcd8 [FEAT] some update 2025-01-06 00:50:38 +01:00
db1e387ac8 [FIX] set work again 2025-01-06 00:27:09 +01:00
762a1aeced [VERSION] update dev tag version 2025-01-05 22:06:58 +01:00
670b5537bb [RELEASE] Release v1.0.0 2025-01-05 22:06:38 +01:00
16e7197939 [FEAT] fix archidata_vertion for release 2025-01-05 22:03:44 +01:00
98d213b01e [FEAT] Bridge 'JDK logging API' to 'SLF4J' 2025-01-05 21:53:42 +01:00
0260b4d408 [FEAT] update to new API of archidata 2025-01-05 20:08:56 +01:00
f03a18c34c [RELEASE] prepare release 2024-09-20 19:51:17 +02:00
6107b95dcd [FEAT] better management of upload files 2024-09-20 22:19:10 +02:00
e9af64405b [DEV] add checker 2024-09-19 21:22:35 +02:00
725cd54d92 [FEAT] basic pop-up and upload a single file (can not read) 2024-09-01 23:10:40 +02:00
08eb0f878a [FEAT] ready gor update and suggestion 2024-09-01 23:10:40 +02:00
619e9aa4b8 [FEAT] update the Slector to support add element 2024-09-16 21:53:29 +02:00
6181022814 [FEAT] add basic page of the ADD media (kanva OK, no upload and check) 2024-09-16 00:06:37 +02:00
3f96b870a0 [FEAT] ready to generate production 2024-09-16 00:05:53 +02:00
5eba6d32b6 [DEV] build docker and correct some typing 2024-09-15 17:58:34 +02:00
a24484b5f9 [FEAT] ignore env_dev/data* 2024-09-15 17:58:34 +02:00
77f8527fc4 [FEAT] rename front2 as front 2024-09-15 17:58:24 +02:00
75cca15898 [FEAT] remove angular front 2024-09-15 17:56:48 +02:00
f0b2a859db [FEAT] correct some connection/disconnection of SSO 2024-09-15 17:56:48 +02:00
a6fe889d4c [FEAT] better covers, Icons and work on better display for the phones 2024-09-15 17:56:48 +02:00
ad7ff2c340 [FEAT] upgrade the api generation and search how to manage cover auto-change 2024-09-15 17:56:48 +02:00
1271885124 [FIX] correct the API generation 2024-09-15 17:56:48 +02:00
dde514a1a0 [FIX] missing migration 2024-09-15 17:56:48 +02:00
dc7131843d [FEAT] continue integration 2024-09-15 17:56:48 +02:00
091390e025 [FEAT] add edit of album and artist
Missing cover that might be studied
2024-09-15 17:56:48 +02:00
3377f80fbc [FEAT] generize the form model 2024-09-15 17:56:48 +02:00
4cf71f35b6 [FEAT] review some namings 2024-09-15 17:56:48 +02:00
faaf3b280b [FEAT] live edit, remove is operational with confirm pop-in.
need to upgrade the code to be reusable
2024-09-15 17:56:48 +02:00
0687369164 [DEV] test 2 2024-09-15 17:56:48 +02:00
147264eb7f [DEV] test 1 2024-09-15 17:56:48 +02:00
ec93c31acb Work on form and basic tools I need 2024-09-15 17:56:48 +02:00
873f0acff5 [DEV] integrate menue on thack 2024-09-15 17:56:48 +02:00
244f8a988e [DEV] remove all async it is too slow... 2024-09-15 17:56:48 +02:00
d49e1d06fe [FEAT] so many things .... 2024-09-15 17:56:48 +02:00
53c3cfefe3 [FEAT] corect the useMemo and better interface of audio player 2024-09-15 17:56:48 +02:00
90fb1ed411 [FEAT] add album direct diaplay 2024-09-15 17:56:48 +02:00
30bc82edb3 [FEAT] basic working karusic 2024-09-15 17:56:40 +02:00
1d4c547d89 [DEV] first vesion of karisic in react + chakra-ui 2024-08-22 11:29:57 +02:00
21d53b77f2 [FEAT] many renaming 2024-08-15 11:47:50 +02:00
3b27edfa3b [VERSION] update dev tag version 2024-05-12 12:03:31 +02:00
6155ca5c0b [RELEASE] Release v0.2.0 2024-05-12 12:03:31 +02:00
45eb56a6fe [DEV] update relase for island 2024-05-12 11:57:08 +02:00
9e0a834b48 [DEV] update readme 2024-04-15 00:57:00 +02:00
d5f335e1e7 [DEV] add progress callback 2024-04-15 00:56:45 +02:00
df384cc8be [DEV] correct version with some ibrary update 2024-04-13 09:03:05 +02:00
4b545ebbe2 [FIX] update dockerfile 2024-04-07 19:25:07 +02:00
eb3cd1b1fe [DEV] all is operational 2024-04-07 19:13:06 +02:00
95e2e03686 [DEV] all work well 2024-04-07 17:08:43 +02:00
2402fc31ed [DEV] it works without security 2024-04-07 17:05:36 +02:00
763875fbdf {DEV] still work 2024-04-07 09:02:17 +02:00
4ff2b247b4 [DEV] update to basic work 2024-04-06 13:08:12 +02:00
e4246750c7 [DEV] Run but does not realy start 2024-04-05 23:51:48 +02:00
04633240b3 [DEV] find how to fix the error but not Ok with the result 2024-04-03 00:34:47 +02:00
c76a339caf [DEV] update new model with remote library (not work) 2024-03-29 18:39:33 +01:00
8a5758af78 [DEV] remove submodule and correct first init 2024-03-23 07:27:27 +01:00
e05fe70f80 [DEV] ready for deply new model of data UUID 2024-03-19 23:17:05 +01:00
f6f9fa199a [DEV] update new version 2024-03-19 19:17:04 +01:00
a1684f0fc0 [DEV] update new model 2024-03-19 08:37:07 +01:00
cebf7d35fb [DEV] update all the model 2024-03-17 23:45:38 +01:00
5d8dab3742 [DEV] finish base of port but not tested 2024-03-15 07:59:23 +01:00
9a3f28553d [DEV] basic migration of to the new generation interface 2024-03-11 00:07:46 +01:00
48e14212db [DEV] implement partial migration to support new uuid for data and local cover of model 2024-03-04 00:14:08 +01:00
c3e2b7240b [DEV] migrate with succes to angular17 2024-02-26 00:01:37 +01:00
0af1bbfb52 [DEV] correct the backend can not insert trach artist ... fail t in archidata model 2024-02-26 00:01:05 +01:00
619973459a [DEV] continue migration to Angular17 2024-02-23 00:37:47 +01:00
d37ce25621 [DEV] update to angular17 2024-02-15 22:59:35 +01:00
b4157877c3 [DEV] add search in artist and album 2024-01-27 19:34:53 +01:00
883866d5f6 [DEV] update display better 2024-01-27 18:09:29 +01:00
247064e412 [DEV] remove test replace 2024-01-25 23:39:41 +01:00
70c63882e4 [DEV] corrrect the desplay of the player overflow (and some redesign) 2024-01-25 23:37:36 +01:00
9da93a2831 [DEV] update dev tag version 2024-01-19 22:50:09 +01:00
358 changed files with 25857 additions and 34936 deletions

3
.gitignore vendored
View File

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

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "front/src/common"]
path = front/src/common
url = ../common_web.git

View File

@@ -1,7 +1,32 @@
#!/bin/bash #!/bin/bash
version_file="../version.txt"
# update new release dependency
cd back cd back
mvn versions:set -DnewVersion=$(cat ../version.txt) # update the Maven version number
mvn versions:set -DnewVersion=$(sed 's/dev/SNAPSHOT/g' $version_file)
if grep -q "DEV" "$version_file"; then
# update all versions release of dependency
mvn versions:use-latest-releases
# update our manage dependency as snapshoot
mvn versions:use-latest-versions -Dincludes=kangaroo-and-rabbit
else
# update our manage dependency as release (must be done before)
mvn versions:use-latest-releases -Dincludes=kangaroo-and-rabbit
fi
cd - cd -
cd front
if grep -q "dev" "$version_file"; then
# update all dependency
pnpm install
pnpm run update_packages
else
# in case of release ==> can not do it automatically ...
echo not implemented
fi
cd -

View File

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

75
README.md Normal file
View File

@@ -0,0 +1,75 @@
Karideo
=======
**K**angaroo **A**nd **R**abbit (m)usic is a simple framework to propose music streaming for personal network
Run in local:
=============
so simple...
```{.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
```
convert in an angular application:
https://betterprogramming.pub/how-to-convert-your-angular-application-to-a-native-mobile-app-android-and-ios-c212b38976df
Link with the external sub-library (front)
------------------------------------------
Link:
```bash
cd front
pnpm run link_kar_cw
```
un-link:
```bash
cd front
pnpm run unlink_kar_cw
```
Manual set in production:
=========================
Connect on the registry
------------------------
To log-in and log-out from the registry:
```bash
export REGISTRY_ADDRESS=gitea.atria-soft.org
docker login -u <<YOUR_USER_NAME>> ${REGISTRY_ADDRESS}
docker logout ${REGISTRY_ADDRESS}
```
pull the root image of dockers
------------------------------
```bash
docker pull archlinux:base-devel
docker pull bellsoft/liberica-openjdk-alpine:latest
```
Create the version
------------------
Execute in the local folder: (use ```dev``` for development and ```latest``` for production release)
```bash
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

@@ -1,4 +0,0 @@

View File

@@ -1,10 +1,10 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <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> <modelVersion>4.0.0</modelVersion>
<groupId>org.kar</groupId> <groupId>org.kar</groupId>
<artifactId>karusic</artifactId> <artifactId>karusic</artifactId>
<version>0.1.0</version> <version>1.0.4</version>
<properties> <properties>
<maven.compiler.version>3.1</maven.compiler.version> <maven.compiler.version>3.1</maven.compiler.version>
<maven.compiler.source>21</maven.compiler.source> <maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target> <maven.compiler.target>21</maven.compiler.target>
@@ -20,13 +20,30 @@
<dependency> <dependency>
<groupId>kangaroo-and-rabbit</groupId> <groupId>kangaroo-and-rabbit</groupId>
<artifactId>archidata</artifactId> <artifactId>archidata</artifactId>
<version>0.6.0</version> <version>0.20.4</version>
</dependency> </dependency>
<!-- Loopback of logger JDK logging API to SLF4J -->
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId> <artifactId>jul-to-slf4j</artifactId>
<version>2.0.9</version> <version>2.0.9</version>
</dependency> </dependency>
<!-- generic logger of SLF4J to console (in color) -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.11</version>
</dependency>
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>2.12.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.18.0-rc1</version>
</dependency>
<!-- <!--
************************************************************ ************************************************************
** TEST dependency ** ** TEST dependency **
@@ -35,15 +52,25 @@
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId> <artifactId>junit-jupiter-api</artifactId>
<version>5.10.1</version> <version>5.11.0</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId> <artifactId>junit-jupiter-engine</artifactId>
<version>5.10.1</version> <version>5.11.0</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>2.24.1</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.5.0</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
<sourceDirectory>src</sourceDirectory> <sourceDirectory>src</sourceDirectory>
@@ -181,23 +208,10 @@
</configuration> </configuration>
</plugin> </plugin>
<!-- Check the style of the code --> <!-- Check the style of the code -->
<!--
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<configLocation>CheckStyle.xml</configLocation>
<consoleOutput>true</consoleOutput>
<failOnViolation>true</failOnViolation>
<failsOnError>true</failsOnError>
<includeTestSourceDirectory>true</includeTestSourceDirectory>
</configuration>
</plugin>
<plugin> <plugin>
<groupId>net.revelc.code.formatter</groupId> <groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId> <artifactId>formatter-maven-plugin</artifactId>
<version>2.12.2</version> <version>2.23.0</version>
<configuration> <configuration>
<encoding>UTF-8</encoding> <encoding>UTF-8</encoding>
<lineEnding>LF</lineEnding> <lineEnding>LF</lineEnding>
@@ -221,7 +235,23 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
--> <plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.8.5.0</version>
<configuration>
<includeFilterFile>spotbugs-security-include.xml</includeFilterFile>
<excludeFilterFile>spotbugs-security-exclude.xml</excludeFilterFile>
<!--<plugins>
<plugin>
<groupId>com.h3xstream.findsecbugs</groupId>
<artifactId>findsecbugs-plugin</artifactId>
<version>1.12.0</version>
</plugin>
</plugins>
-->
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
<!-- Generate Java-docs As Part Of Project Reports --> <!-- Generate Java-docs As Part Of Project Reports -->

258
back/pom.xml.versionsBackup Normal file
View File

@@ -0,0 +1,258 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.kar</groupId>
<artifactId>karusic</artifactId>
<version>0.1.0</version>
<properties>
<maven.compiler.version>3.1</maven.compiler.version>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<maven.dependency.version>3.1.1</maven.dependency.version>
</properties>
<repositories>
<repository>
<id>gitea</id>
<url>https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/maven</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>kangaroo-and-rabbit</groupId>
<artifactId>archidata</artifactId>
<version>0.14.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.1.0-alpha1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.18.0-rc1</version>
</dependency>
<!--
************************************************************
** TEST dependency **
************************************************************
-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.11.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.11.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>2.24.1</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.5.0</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<testSourceDirectory>test/src</testSourceDirectory>
<directory>${project.basedir}/out/maven/</directory>
<resources>
<resource>
<directory>src/resources</directory>
</resource>
</resources>
<testResources>
<testResource>
<directory>${basedir}/test/resources</directory>
</testResource>
</testResources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.version}</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.4.0</version>
<configuration>
<mainClass>org.kar.karusic.WebLauncher</mainClass>
</configuration>
</plugin>
<!-- Create the source bundle -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- junit results -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>fully.qualified.MainClass</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
<!-- Create coverage -->
<!--
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.10</version>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>jacoco-check</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<element>PACKAGE</element>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>0.50</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
-->
<!-- Java-doc generation for stand-alone site -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<show>private</show>
<nohelp>true</nohelp>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>exec-application</id>
<phase>package</phase>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>org.kar.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>
<configuration>
<encoding>UTF-8</encoding>
<lineEnding>LF</lineEnding>
<configFile>Formatter.xml</configFile>
<directories>
<directory>src/</directory>
<directory>test/src</directory>
</directories>
<includes>
<include>**/*.java</include>
</includes>
<excludes>
<exclude>module-info.java</exclude>
</excludes>
</configuration>
<executions>
<execution>
<goals>
<goal>validate</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.8.5.0</version>
<configuration>
<includeFilterFile>spotbugs-security-include.xml</includeFilterFile>
<excludeFilterFile>spotbugs-security-exclude.xml</excludeFilterFile>
<!--<plugins>
<plugin>
<groupId>com.h3xstream.findsecbugs</groupId>
<artifactId>findsecbugs-plugin</artifactId>
<version>1.12.0</version>
</plugin>
</plugins>
-->
</configuration>
</plugin>
</plugins>
</build>
<!-- Generate Java-docs As Part Of Project Reports -->
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<show>public</show>
</configuration>
</plugin>
</plugins>
</reporting>
</project>

View File

@@ -0,0 +1,27 @@
package org.kar.karusic.CodecBson;
import java.util.UUID;
import org.bson.BsonReader;
import org.bson.BsonWriter;
import org.bson.codecs.Codec;
import org.bson.codecs.DecoderContext;
import org.bson.codecs.EncoderContext;
public class UUIDCodec implements Codec<UUID> {
@Override
public UUID decode(final BsonReader reader, final DecoderContext decoderContext) {
return UUID.fromString(reader.readString());
}
@Override
public void encode(final BsonWriter writer, final UUID value, final EncoderContext encoderContext) {
writer.writeString(value.toString());
}
@Override
public Class<UUID> getEncoderClass() {
return UUID.class;
}
}

View File

@@ -1,23 +1,29 @@
package org.kar.karusic; package org.kar.karusic;
import java.net.URI; import java.net.URI;
import java.util.Iterator;
import java.util.logging.LogManager;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriter;
import org.glassfish.grizzly.http.server.HttpServer; import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory; import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.jackson.JacksonFeature; import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.media.multipart.MultiPartFeature; import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.server.ResourceConfig;
import org.kar.archidata.GlobalConfiguration;
import org.kar.archidata.UpdateJwtPublicKey; import org.kar.archidata.UpdateJwtPublicKey;
import org.kar.archidata.api.DataResource; import org.kar.archidata.api.DataResource;
import org.kar.archidata.catcher.ExceptionCatcher; import org.kar.archidata.api.ProxyResource;
import org.kar.archidata.catcher.FailExceptionCatcher; import org.kar.archidata.catcher.GenericCatcher;
import org.kar.archidata.catcher.InputExceptionCatcher; import org.kar.archidata.db.DbConfig;
import org.kar.archidata.catcher.SystemExceptionCatcher; import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.filter.CORSFilter; import org.kar.archidata.filter.CORSFilter;
import org.kar.archidata.filter.OptionFilter; import org.kar.archidata.filter.OptionFilter;
import org.kar.archidata.migration.MigrationEngine; import org.kar.archidata.migration.MigrationEngine;
import org.kar.archidata.tools.ConfigBaseVariable; import org.kar.archidata.tools.ConfigBaseVariable;
import org.kar.archidata.tools.ContextGenericTools;
import org.kar.karusic.api.AlbumResource; import org.kar.karusic.api.AlbumResource;
import org.kar.karusic.api.ArtistResource; import org.kar.karusic.api.ArtistResource;
import org.kar.karusic.api.Front; import org.kar.karusic.api.Front;
@@ -29,8 +35,13 @@ import org.kar.karusic.api.UserResource;
import org.kar.karusic.filter.KarusicAuthenticationFilter; import org.kar.karusic.filter.KarusicAuthenticationFilter;
import org.kar.karusic.migration.Initialization; import org.kar.karusic.migration.Initialization;
import org.kar.karusic.migration.Migration20231126; 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.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;
import jakarta.ws.rs.core.UriBuilder; import jakarta.ws.rs.core.UriBuilder;
@@ -38,15 +49,15 @@ public class WebLauncher {
final static Logger LOGGER = LoggerFactory.getLogger(WebLauncher.class); final static Logger LOGGER = LoggerFactory.getLogger(WebLauncher.class);
protected UpdateJwtPublicKey keyUpdater = null; protected UpdateJwtPublicKey keyUpdater = null;
protected HttpServer server = null; protected HttpServer server = null;
public WebLauncher() { public WebLauncher() {
ConfigBaseVariable.bdDatabase = "karusic"; ConfigBaseVariable.bdDatabase = "karusic";
} }
private static URI getBaseURI() { private static URI getBaseURI() {
return UriBuilder.fromUri(ConfigBaseVariable.getlocalAddress()).build(); return UriBuilder.fromUri(ConfigBaseVariable.getlocalAddress()).build();
} }
public void migrateDB() throws Exception { public void migrateDB() throws Exception {
WebLauncher.LOGGER.info("Create migration engine"); WebLauncher.LOGGER.info("Create migration engine");
final MigrationEngine migrationEngine = new MigrationEngine(); final MigrationEngine migrationEngine = new MigrationEngine();
@@ -54,16 +65,24 @@ public class WebLauncher {
migrationEngine.setInit(new Initialization()); migrationEngine.setInit(new Initialization());
WebLauncher.LOGGER.info("Add migration since last version"); WebLauncher.LOGGER.info("Add migration since last version");
migrationEngine.add(new Migration20231126()); migrationEngine.add(new Migration20231126());
migrationEngine.add(new Migration20240225());
migrationEngine.add(new Migration20240226());
migrationEngine.add(new Migration20240907());
migrationEngine.add(new Migration20250104());
WebLauncher.LOGGER.info("Migrate the DB [START]"); WebLauncher.LOGGER.info("Migrate the DB [START]");
migrationEngine.migrateWaitAdmin(GlobalConfiguration.dbConfig); migrationEngine.migrateWaitAdmin(new DbConfig());
WebLauncher.LOGGER.info("Migrate the DB [STOP]"); WebLauncher.LOGGER.info("Migrate the DB [STOP]");
} }
public static void main(final String[] args) throws Exception { public static void main(final String[] args) throws Exception {
// Loop-back of logger JDK logging API to SLF4J
LogManager.getLogManager().reset();
SLF4JBridgeHandler.install();
WebLauncher.LOGGER.info("[START] application wake UP"); WebLauncher.LOGGER.info("[START] application wake UP");
final WebLauncher launcher = new WebLauncher(); final WebLauncher launcher = new WebLauncher();
launcher.migrateDB(); launcher.migrateDB();
launcher.process(); launcher.process();
WebLauncher.LOGGER.info("end-configure the server & wait finish process:"); WebLauncher.LOGGER.info("end-configure the server & wait finish process:");
Thread.currentThread().join(); Thread.currentThread().join();
@@ -71,13 +90,40 @@ public class WebLauncher {
launcher.stopOther(); launcher.stopOther();
WebLauncher.LOGGER.info("STOP the REST server:"); WebLauncher.LOGGER.info("STOP the REST server:");
} }
public void process() throws InterruptedException { public void plop(final String aaa) {
// List available Image Readers
WebLauncher.LOGGER.trace("Available Image Readers:");
final Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName(aaa);
while (readers.hasNext()) {
final ImageReader reader = readers.next();
WebLauncher.LOGGER.trace("Reader: " + reader.getOriginatingProvider().getDescription(null));
WebLauncher.LOGGER.trace("Reader CN: " + reader.getOriginatingProvider().getPluginClassName());
// ImageIO.deregisterServiceProvider(reader.getOriginatingProvider());
}
// List available Image Writers
WebLauncher.LOGGER.trace("\nAvailable Image Writers:");
final Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(aaa);
while (writers.hasNext()) {
final ImageWriter writer = writers.next();
WebLauncher.LOGGER.trace("Writer: " + writer.getOriginatingProvider().getDescription(null));
WebLauncher.LOGGER.trace("Writer CN: " + writer.getOriginatingProvider().getPluginClassName());
}
}
public void process() throws InterruptedException, DataAccessException {
ImageIO.scanForPlugins();
plop("jpeg");
plop("png");
plop("webmp");
plop("webp");
// =================================================================== // ===================================================================
// Configure resources // Configure resources
// =================================================================== // ===================================================================
final ResourceConfig rc = new ResourceConfig(); final ResourceConfig rc = new ResourceConfig();
// add multipart models .. // add multipart models ..
rc.register(MultiPartFeature.class); rc.register(MultiPartFeature.class);
// global authentication system // global authentication system
@@ -87,10 +133,7 @@ public class WebLauncher {
// global authentication system // global authentication system
rc.register(KarusicAuthenticationFilter.class); rc.register(KarusicAuthenticationFilter.class);
// register exception catcher // register exception catcher
rc.register(InputExceptionCatcher.class); GenericCatcher.addAll(rc);
rc.register(SystemExceptionCatcher.class);
rc.register(FailExceptionCatcher.class);
rc.register(ExceptionCatcher.class);
// add default resource: // add default resource:
rc.register(UserResource.class); rc.register(UserResource.class);
rc.register(AlbumResource.class); rc.register(AlbumResource.class);
@@ -99,22 +142,25 @@ public class WebLauncher {
rc.register(PlaylistResource.class); rc.register(PlaylistResource.class);
rc.register(TrackResource.class); rc.register(TrackResource.class);
rc.register(DataResource.class); rc.register(DataResource.class);
rc.register(ProxyResource.class);
rc.register(HealthCheck.class); rc.register(HealthCheck.class);
rc.register(Front.class); rc.register(Front.class);
ContextGenericTools.addJsr310(rc);
// add jackson to be discover when we are ins standalone server // add jackson to be discover when we are ins standalone server
rc.register(JacksonFeature.class); rc.register(JacksonFeature.class);
// enable this to show low level request // enable this to show low level request
//rc.property(LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_SERVER, Level.WARNING.getName()); // rc.property(LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_SERVER, Level.WARNING.getName());
//System.out.println("Connect on the BDD:"); // System.out.println("Connect on the BDD:");
//System.out.println(" getDBHost: '" + ConfigVariable.getDBHost() + "'"); // System.out.println(" getDBHost: '" + ConfigVariable.getDBHost() + "'");
//System.out.println(" getDBPort: '" + ConfigVariable.getDBPort() + "'"); // System.out.println(" getDBPort: '" + ConfigVariable.getDBPort() + "'");
//System.out.println(" getDBLogin: '" + ConfigVariable.getDBLogin() + "'"); // System.out.println(" getDBLogin: '" + ConfigVariable.getDBLogin() + "'");
//System.out.println(" getDBPassword: '" + ConfigVariable.getDBPassword() + "'"); // System.out.println(" getDBPassword: '" + ConfigVariable.getDBPassword() + "'");
//System.out.println(" getDBName: '" + ConfigVariable.getDBName() + "'"); // System.out.println(" getDBName: '" + ConfigVariable.getDBName() + "'");
System.out.println(" ==> " + GlobalConfiguration.dbConfig); System.out.println(" ==> " + new DbConfig());
System.out.println("OAuth service " + getBaseURI()); System.out.println("OAuth service " + getBaseURI());
this.server = GrizzlyHttpServerFactory.createHttpServer(getBaseURI(), rc); this.server = GrizzlyHttpServerFactory.createHttpServer(getBaseURI(), rc);
final HttpServer serverLink = this.server; final HttpServer serverLink = this.server;
@@ -125,13 +171,13 @@ public class WebLauncher {
serverLink.shutdownNow(); serverLink.shutdownNow();
} }
}, "shutdownHook")); }, "shutdownHook"));
// =================================================================== // ===================================================================
// start periodic update of the token ... // start periodic update of the token ...
// =================================================================== // ===================================================================
this.keyUpdater = new UpdateJwtPublicKey(); this.keyUpdater = new UpdateJwtPublicKey();
this.keyUpdater.start(); this.keyUpdater.start();
// =================================================================== // ===================================================================
// run JERSEY // run JERSEY
// =================================================================== // ===================================================================
@@ -143,14 +189,14 @@ public class WebLauncher {
e.printStackTrace(); e.printStackTrace();
} }
} }
public void stop() { public void stop() {
if (this.server != null) { if (this.server != null) {
this.server.shutdownNow(); this.server.shutdownNow();
this.server = null; this.server = null;
} }
} }
public void stopOther() { public void stopOther() {
this.keyUpdater.kill(); this.keyUpdater.kill();
try { try {

View File

@@ -1,29 +1,62 @@
package org.kar.karusic; 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.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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;
public class WebLauncherLocal extends WebLauncher { public class WebLauncherLocal extends WebLauncher {
final Logger logger = LoggerFactory.getLogger(WebLauncherLocal.class); private static final Logger LOGGER = LoggerFactory.getLogger(WebLauncherLocal.class);
private WebLauncherLocal() {} private WebLauncherLocal() {}
public static void main(final String[] args) throws InterruptedException { 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(); final WebLauncherLocal launcher = new WebLauncherLocal();
launcher.process(); launcher.process();
launcher.logger.info("end-configure the server & wait finish process:"); launcher.LOGGER.info("end-configure the server & wait finish process:");
Thread.currentThread().join(); Thread.currentThread().join();
launcher.logger.info("STOP the REST server:"); launcher.LOGGER.info("STOP the REST server:");
} }
@Override @Override
public void process() throws InterruptedException { public void process() throws InterruptedException, DataAccessException {
if (true) { if (true) {
// for local test: // for local test:
ConfigBaseVariable.apiAdress = "http://0.0.0.0:19080/karusic/api/"; ConfigBaseVariable.apiAdress = "http://0.0.0.0:19080/karusic/api/";
//ConfigBaseVariable.ssoAdress = "https://atria-soft.org/karso/api/"; // ConfigBaseVariable.ssoAdress = "https://atria-soft.org/karso/api/";
ConfigBaseVariable.dbPort = "3906"; ConfigBaseVariable.dbPort = "3906";
ConfigBaseVariable.testMode = "true";
} }
try { try {
super.migrateDB(); super.migrateDB();

View File

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

View File

@@ -3,12 +3,21 @@ package org.kar.karusic.api;
import java.io.InputStream; import java.io.InputStream;
import java.util.List; import java.util.List;
import org.bson.types.ObjectId;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam; 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.DataAccess;
import org.kar.archidata.dataAccess.addOn.AddOnManyToMany; import org.kar.archidata.dataAccess.addOnSQL.AddOnDataJson;
import org.kar.archidata.dataAccess.options.CheckFunction;
import org.kar.archidata.tools.DataTools; import org.kar.archidata.tools.DataTools;
import org.kar.karusic.model.Artist; 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.annotation.security.RolesAllowed;
import jakarta.ws.rs.Consumes; import jakarta.ws.rs.Consumes;
@@ -20,63 +29,73 @@ import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam; import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces; import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
@Path("/artist") @Path("/artist")
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public class ArtistResource { public class ArtistResource {
private static final Logger LOGGER = LoggerFactory.getLogger(ArtistResource.class);
static final ArtistChecker CHECKER = new ArtistChecker();
@GET @GET
@Path("{id}") @Path("{id}")
@RolesAllowed("USER") @RolesAllowed("USER")
public static Artist getWithId(@PathParam("id") final Long id) throws Exception { public Artist get(@PathParam("id") final Long id) throws Exception {
return DataAccess.get(Artist.class, id); return DataAccess.get(Artist.class, id);
} }
@GET @GET
@RolesAllowed("USER") @RolesAllowed("USER")
public List<Artist> get() throws Exception { public List<Artist> gets() throws Exception {
return DataAccess.gets(Artist.class); return DataAccess.gets(Artist.class);
} }
@POST @POST
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
public Artist put(final String jsonRequest) throws Exception { public Artist post(final Artist data) throws Exception {
return DataAccess.insertWithJson(Artist.class, jsonRequest); return DataAccess.insert(data, new CheckFunction(CHECKER));
} }
@PATCH @PATCH
@Path("{id}") @Path("{id}")
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
public Artist put(@PathParam("id") final Long id, final String jsonRequest) throws Exception { public Artist patch(@PathParam("id") final Long id, @AsyncType(Artist.class) final String jsonRequest) throws Exception {
DataAccess.updateWithJson(Artist.class, id, jsonRequest); DataAccess.updateWithJson(Artist.class, id, jsonRequest, new CheckFunction(CHECKER));
return DataAccess.get(Artist.class, id); return DataAccess.get(Artist.class, id);
} }
@DELETE @DELETE
@Path("{id}") @Path("{id}")
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
public Response delete(@PathParam("id") final Long id) throws Exception { public void remove(@PathParam("id") final Long id) throws Exception {
DataAccess.delete(Artist.class, id); DataAccess.delete(Artist.class, id);
return Response.ok().build();
} }
@POST @POST
@Path("{id}/add_cover") @Path("{id}/cover")
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA }) @Consumes({ MediaType.MULTIPART_FORM_DATA })
public Response uploadCover(@PathParam("id") final Long id, @FormDataParam("fileName") final String fileName, @FormDataParam("file") final InputStream fileInputStream, @TypeScriptProgress
@FormDataParam("file") final FormDataContentDisposition fileMetaData) { public Artist uploadCover(@PathParam("id") final Long id, @FormDataOptional @FormDataParam("uri") final String uri, @FormDataOptional @FormDataParam("file") final InputStream fileInputStream,
return DataTools.uploadCover(Artist.class, id, fileName, fileInputStream, fileMetaData); @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);
}
} }
@GET @DELETE
@Path("{id}/rm_cover/{coverId}") @Path("{id}/cover/{coverId}")
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
public Response removeCover(@PathParam("id") final Long id, @PathParam("coverId") final Long coverId) throws Exception { public Artist removeCover(@PathParam("id") final Long id, @PathParam("coverId") final ObjectId coverId) throws Exception {
AddOnManyToMany.removeLink(Artist.class, id, "cover", coverId); try (DBAccess db = DBAccess.createInterface()) {
return Response.ok(DataAccess.get(Artist.class, id)).build(); AddOnDataJson.removeLink(db, Artist.class, "id", id, "covers", coverId);
return db.get(Artist.class, id);
}
} }
} }

View File

@@ -3,12 +3,21 @@ package org.kar.karusic.api;
import java.io.InputStream; import java.io.InputStream;
import java.util.List; import java.util.List;
import org.bson.types.ObjectId;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam; 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.DataAccess;
import org.kar.archidata.dataAccess.addOn.AddOnManyToMany; import org.kar.archidata.dataAccess.addOnSQL.AddOnDataJson;
import org.kar.archidata.dataAccess.options.CheckFunction;
import org.kar.archidata.tools.DataTools; import org.kar.archidata.tools.DataTools;
import org.kar.karusic.model.Gender; 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.annotation.security.RolesAllowed;
import jakarta.ws.rs.Consumes; import jakarta.ws.rs.Consumes;
@@ -20,63 +29,73 @@ import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam; import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces; import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
@Path("/gender") @Path("/gender")
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public class GenderResource { public class GenderResource {
private static final Logger LOGGER = LoggerFactory.getLogger(GenderResource.class);
static final GenderChecker CHECKER = new GenderChecker();
@GET @GET
@Path("{id}") @Path("{id}")
@RolesAllowed("USER") @RolesAllowed("USER")
public static Gender getWithId(@PathParam("id") final Long id) throws Exception { public Gender get(@PathParam("id") final Long id) throws Exception {
return DataAccess.get(Gender.class, id); return DataAccess.get(Gender.class, id);
} }
@GET @GET
@RolesAllowed("USER") @RolesAllowed("USER")
public List<Gender> get() throws Exception { public List<Gender> gets() throws Exception {
return DataAccess.gets(Gender.class); return DataAccess.gets(Gender.class);
} }
@POST @POST
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
public Gender put(final String jsonRequest) throws Exception { public Gender post(final Gender data) throws Exception {
return DataAccess.insertWithJson(Gender.class, jsonRequest); return DataAccess.insert(data, new CheckFunction(CHECKER));
} }
@PATCH @PATCH
@Path("{id}") @Path("{id}")
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
public Gender put(@PathParam("id") final Long id, final String jsonRequest) throws Exception { public Gender patch(@PathParam("id") final Long id, @AsyncType(Gender.class) final String jsonRequest) throws Exception {
DataAccess.updateWithJson(Gender.class, id, jsonRequest); DataAccess.updateWithJson(Gender.class, id, jsonRequest, new CheckFunction(CHECKER));
return DataAccess.get(Gender.class, id); return DataAccess.get(Gender.class, id);
} }
@DELETE @DELETE
@Path("{id}") @Path("{id}")
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
public Response delete(@PathParam("id") final Long id) throws Exception { public void remove(@PathParam("id") final Long id) throws Exception {
DataAccess.delete(Gender.class, id); DataAccess.delete(Gender.class, id);
return Response.ok().build();
} }
@POST @POST
@Path("{id}/add_cover") @Path("{id}/cover")
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA }) @Consumes({ MediaType.MULTIPART_FORM_DATA })
public Response uploadCover(@PathParam("id") final Long id, @FormDataParam("fileName") final String fileName, @FormDataParam("file") final InputStream fileInputStream, @TypeScriptProgress
@FormDataParam("file") final FormDataContentDisposition fileMetaData) { public Gender uploadCover(@PathParam("id") final Long id, @FormDataOptional @FormDataParam("uri") final String uri, @FormDataOptional @FormDataParam("file") final InputStream fileInputStream,
return DataTools.uploadCover(Gender.class, id, fileName, fileInputStream, fileMetaData); @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);
}
} }
@GET @DELETE
@Path("{id}/rm_cover/{coverId}") @Path("{id}/cover/{coverId}")
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
public Response removeCover(@PathParam("id") final Long id, @PathParam("coverId") final Long coverId) throws Exception { public Gender removeCover(@PathParam("id") final Long id, @PathParam("coverId") final ObjectId coverId) throws Exception {
AddOnManyToMany.removeLink(Gender.class, id, "cover", coverId); try (DBAccess db = DBAccess.createInterface()) {
return Response.ok(DataAccess.get(Gender.class, id)).build(); AddOnDataJson.removeLink(db, Gender.class, "id", id, "covers", coverId);
return db.get(Gender.class, id);
}
} }
} }

View File

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

View File

@@ -3,12 +3,19 @@ package org.kar.karusic.api;
import java.io.InputStream; import java.io.InputStream;
import java.util.List; import java.util.List;
import org.bson.types.ObjectId;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam; 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.DataAccess;
import org.kar.archidata.dataAccess.addOn.AddOnManyToMany; import org.kar.archidata.dataAccess.addOnSQL.AddOnDataJson;
import org.kar.archidata.dataAccess.options.CheckFunction;
import org.kar.archidata.tools.DataTools; import org.kar.archidata.tools.DataTools;
import org.kar.karusic.model.Playlist; 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.annotation.security.RolesAllowed;
import jakarta.ws.rs.Consumes; import jakarta.ws.rs.Consumes;
@@ -20,80 +27,89 @@ import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam; import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces; import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
@Path("/playlist") @Path("/playlist")
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public class PlaylistResource { public class PlaylistResource {
private static final Logger LOGGER = LoggerFactory.getLogger(PlaylistResource.class);
static final TrackChecker CHECKER = new TrackChecker();
@GET @GET
@Path("{id}") @Path("{id}")
@RolesAllowed("USER") @RolesAllowed("USER")
public static Playlist getWithId(@PathParam("id") final Long id) throws Exception { public Playlist get(@PathParam("id") final Long id) throws Exception {
return DataAccess.get(Playlist.class, id); return DataAccess.get(Playlist.class, id);
} }
@GET @GET
@RolesAllowed("USER") @RolesAllowed("USER")
public List<Playlist> get() throws Exception { public List<Playlist> gets() throws Exception {
return DataAccess.gets(Playlist.class); return DataAccess.gets(Playlist.class);
} }
@POST @POST
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
public Playlist put(final String jsonRequest) throws Exception { public Playlist post(final Playlist data) throws Exception {
return DataAccess.insertWithJson(Playlist.class, jsonRequest); return DataAccess.insert(data, new CheckFunction(CHECKER));
} }
@PATCH @PATCH
@Path("{id}") @Path("{id}")
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
public Playlist put(@PathParam("id") final Long id, final String jsonRequest) throws Exception { public Playlist patch(@PathParam("id") final Long id, @AsyncType(Playlist.class) final String jsonRequest) throws Exception {
DataAccess.updateWithJson(Playlist.class, id, jsonRequest); DataAccess.updateWithJson(Playlist.class, id, jsonRequest, new CheckFunction(CHECKER));
return DataAccess.get(Playlist.class, id); return DataAccess.get(Playlist.class, id);
} }
@DELETE @DELETE
@Path("{id}") @Path("{id}")
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
public Response delete(@PathParam("id") final Long id) throws Exception { public void remove(@PathParam("id") final Long id) throws Exception {
DataAccess.delete(Playlist.class, id); DataAccess.delete(Playlist.class, id);
return Response.ok().build();
} }
@POST @POST
@Path("{id}/add_track/{trackId}") @Path("{id}/track/{trackId}")
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA }) @Consumes({ MediaType.MULTIPART_FORM_DATA })
public Playlist addTrack(@PathParam("id") final Long id, @PathParam("trackId") final Long trackId) throws Exception { public Playlist addTrack(@PathParam("id") final Long id, @PathParam("trackId") final Long trackId) throws Exception {
AddOnManyToMany.removeLink(Playlist.class, id, "track", trackId); try (DBAccess db = DBAccess.createInterface()) {
return DataAccess.get(Playlist.class, id); AddOnDataJson.removeLink(db, Playlist.class, "id", id, "track", trackId);
return db.get(Playlist.class, id);
}
} }
@GET @DELETE
@Path("{id}/rm_track/{trackId}") @Path("{id}/track/{trackId}")
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
public Playlist removeTrack(@PathParam("id") final Long id, @PathParam("trackId") final Long trackId) throws Exception { public Playlist removeTrack(@PathParam("id") final Long id, @PathParam("trackId") final Long trackId) throws Exception {
AddOnManyToMany.removeLink(Playlist.class, id, "track", trackId); try (DBAccess db = DBAccess.createInterface()) {
return DataAccess.get(Playlist.class, id); AddOnDataJson.removeLink(db, Playlist.class, "id", id, "track", trackId);
return db.get(Playlist.class, id);
}
} }
@POST @POST
@Path("{id}/add_cover") @Path("{id}/cover")
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA }) @Consumes({ MediaType.MULTIPART_FORM_DATA })
public Response uploadCover(@PathParam("id") final Long id, @FormDataParam("fileName") final String fileName, @FormDataParam("file") final InputStream fileInputStream, @AsyncType(Playlist.class)
@FormDataParam("file") final FormDataContentDisposition fileMetaData) { public void uploadCover(@PathParam("id") final Long id, @FormDataParam("file") final InputStream fileInputStream, @FormDataParam("file") final FormDataContentDisposition fileMetaData)
return DataTools.uploadCover(Playlist.class, id, fileName, fileInputStream, fileMetaData); throws Exception {
try (DBAccess db = DBAccess.createInterface()) {
DataTools.uploadCover(db, Playlist.class, id, fileInputStream, fileMetaData);
}
} }
@GET @DELETE
@Path("{id}/rm_cover/{coverId}") @Path("{id}/cover/{coverId}")
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
public Response removeCover(@PathParam("id") final Long id, @PathParam("coverId") final Long coverId) throws Exception { public Playlist removeCover(@PathParam("id") final Long id, @PathParam("coverId") final ObjectId coverId) throws Exception {
AddOnManyToMany.removeLink(Playlist.class, id, "cover", coverId); try (DBAccess db = DBAccess.createInterface()) {
return Response.ok(DataAccess.get(Playlist.class, id)).build(); AddOnDataJson.removeLink(db, Playlist.class, "id", id, "covers", coverId);
return DataAccess.get(Playlist.class, id);
}
} }
} }

View File

@@ -6,18 +6,22 @@ import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.bson.types.ObjectId;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam; 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.DataAccess;
import org.kar.archidata.dataAccess.QueryCondition; import org.kar.archidata.dataAccess.addOnSQL.AddOnDataJson;
import org.kar.archidata.dataAccess.addOn.AddOnManyToMany; import org.kar.archidata.dataAccess.options.CheckFunction;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.model.Data; import org.kar.archidata.model.Data;
import org.kar.archidata.tools.DataTools; import org.kar.archidata.tools.DataTools;
import org.kar.karusic.model.Album;
import org.kar.karusic.model.Artist;
import org.kar.karusic.model.Gender;
import org.kar.karusic.model.Track; import org.kar.karusic.model.Track;
import org.kar.karusic.model.Track.TrackChecker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.annotation.security.RolesAllowed; import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.Consumes; import jakarta.ws.rs.Consumes;
@@ -34,123 +38,135 @@ import jakarta.ws.rs.core.Response;
@Path("/track") @Path("/track")
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public class TrackResource { public class TrackResource {
private static final Logger LOGGER = LoggerFactory.getLogger(TrackResource.class);
static final TrackChecker CHECKER = new TrackChecker();
@GET @GET
@Path("{id}") @Path("{id}")
@RolesAllowed("USER") @RolesAllowed("USER")
public static Track getWithId(@PathParam("id") final Long id) throws Exception { public Track get(@PathParam("id") final Long id) throws Exception {
return DataAccess.get(Track.class, id); return DataAccess.get(Track.class, id);
} }
@GET @GET
@RolesAllowed("USER") @RolesAllowed("USER")
public List<Track> get() throws Exception { public List<Track> gets() throws Exception {
return DataAccess.gets(Track.class); return DataAccess.gets(Track.class);
} }
@POST @POST
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
public Track create(final String jsonRequest) throws Exception { public Track post(final Track data) throws Exception {
return DataAccess.insertWithJson(Track.class, jsonRequest); return DataAccess.insert(data, new CheckFunction(CHECKER));
} }
@PATCH @PATCH
@Path("{id}") @Path("{id}")
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
public Track put(@PathParam("id") final Long id, final String jsonRequest) throws Exception { public Track patch(@PathParam("id") final Long id, @AsyncType(Track.class) final String jsonRequest) throws Exception {
DataAccess.updateWithJson(Track.class, id, jsonRequest); DataAccess.updateWithJson(Track.class, id, jsonRequest, new CheckFunction(CHECKER));
return DataAccess.get(Track.class, id); return DataAccess.get(Track.class, id);
} }
@DELETE @DELETE
@Path("{id}") @Path("{id}")
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
public Response delete(@PathParam("id") final Long id) throws Exception { public void remove(@PathParam("id") final Long id) throws Exception {
DataAccess.delete(Track.class, id); DataAccess.delete(Track.class, id);
return Response.ok().build();
} }
@POST @POST
@Path("{id}/add_artist/{artistId}") @Path("{id}/artist/{artistId}")
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA }) @Consumes({ MediaType.MULTIPART_FORM_DATA })
public Track addTrack(@PathParam("id") final Long id, @PathParam("artistId") final Long artistId) throws Exception { public Track addTrack(@PathParam("id") final Long id, @PathParam("artistId") final Long artistId) throws Exception {
AddOnManyToMany.removeLink(Track.class, id, "artist", artistId); try (DBAccess db = DBAccess.createInterface()) {
return DataAccess.get(Track.class, id); AddOnDataJson.removeLink(db, Track.class, "id", id, "artist", artistId);
return DataAccess.get(Track.class, id);
}
} }
@GET @DELETE
@Path("{id}/rm_artist/{trackId}") @Path("{id}/artist/{trackId}")
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
public Track removeTrack(@PathParam("id") final Long id, @PathParam("artistId") final Long artistId) throws Exception { public Track removeTrack(@PathParam("id") final Long id, @PathParam("artistId") final Long artistId) throws Exception {
AddOnManyToMany.removeLink(Track.class, id, "artist", artistId); try (DBAccess db = DBAccess.createInterface()) {
return DataAccess.get(Track.class, id); AddOnDataJson.removeLink(db, Track.class, "id", id, "artist", artistId);
return DataAccess.get(Track.class, id);
}
} }
@POST @POST
@Path("{id}/add_cover") @Path("{id}/cover")
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA }) @Consumes({ MediaType.MULTIPART_FORM_DATA })
public Response uploadCover(@PathParam("id") final Long id, @FormDataParam("fileName") final String fileName, @FormDataParam("file") final InputStream fileInputStream, @TypeScriptProgress
@FormDataParam("file") final FormDataContentDisposition fileMetaData) { public Track uploadCover(@PathParam("id") final Long id, @FormDataParam("uri") final String uri, @FormDataParam("file") final InputStream fileInputStream,
return DataTools.uploadCover(Track.class, id, fileName, fileInputStream, fileMetaData); @FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
} try (DBAccess db = DBAccess.createInterface()) {
if (uri != null) {
@GET DataTools.uploadCoverFromUri(db, Track.class, id, uri);
@Path("{id}/rm_cover/{coverId}") } else {
@RolesAllowed("ADMIN") DataTools.uploadCover(db, Track.class, id, fileInputStream, fileMetaData);
public Response removeCover(@PathParam("id") final Long id, @PathParam("coverId") final Long coverId) throws Exception {
AddOnManyToMany.removeLink(Track.class, id, "cover", coverId);
return Response.ok(DataAccess.get(Track.class, id)).build();
}
@POST
@Path("/upload/")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
public Response uploadFile(@FormDataParam("fileName") String fileName, @FormDataParam("gender") String gender, @FormDataParam("artist") String artist,
//@FormDataParam("seriesId") String seriesId, Not used ...
@FormDataParam("album") String album, @FormDataParam("trackId") String trackId, @FormDataParam("title") String title, @FormDataParam("file") final InputStream fileInputStream,
@FormDataParam("file") final FormDataContentDisposition fileMetaData) {
try {
// correct input string stream :
fileName = DataTools.multipartCorrection(fileName);
gender = DataTools.multipartCorrection(gender);
artist = DataTools.multipartCorrection(artist);
album = DataTools.multipartCorrection(album);
trackId = DataTools.multipartCorrection(trackId);
title = DataTools.multipartCorrection(title);
//public NodeSmall uploadFile(final FormDataMultiPart form) {
System.out.println("Upload media file: " + fileMetaData);
System.out.println(" - fileName: " + fileName);
System.out.println(" - gender: " + gender);
System.out.println(" - artist: " + artist);
System.out.println(" - album: " + album);
System.out.println(" - trackId: " + trackId);
System.out.println(" - title: " + title);
System.out.println(" - fileInputStream: " + fileInputStream);
System.out.println(" - fileMetaData: " + fileMetaData);
System.out.flush();
/*
if (typeId == null) {
return Response.status(406).
entity("Missong Input 'type'").
type("text/plain").
build();
} }
*/ return DataAccess.get(Track.class, id);
}
}
@DELETE
@Path("{id}/cover/{coverId}")
@RolesAllowed("ADMIN")
public Track removeCover(@PathParam("id") final Long id, @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);
}
}
@POST
@Path("upload/")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@AsyncType(Track.class)
@TypeScriptProgress
public Response uploadTrack( //
@FormDataParam("title") String title, //
@FormDataOptional @AsyncType(Long.class) @FormDataParam("genderId") String genderId, //
@FormDataOptional @AsyncType(Long.class) @FormDataParam("artistId") String artistId, //
@FormDataOptional @AsyncType(Long.class) @FormDataParam("albumId") String albumId, //
@FormDataOptional @AsyncType(Long.class) @FormDataParam("trackId") String trackId, //
@FormDataParam("file") final InputStream fileInputStream, //
@FormDataParam("file") final FormDataContentDisposition fileMetaData //
) {
try (DBAccess db = DBAccess.createInterface()) {
// correct input string stream :
trackId = DataTools.multipartCorrection(trackId);
albumId = DataTools.multipartCorrection(albumId);
artistId = DataTools.multipartCorrection(artistId);
genderId = DataTools.multipartCorrection(genderId);
title = DataTools.multipartCorrection(title);
// public NodeSmall uploadFile(final FormDataMultiPart form) {
LOGGER.info("Upload media file: " + fileMetaData);
LOGGER.info(" > genderId: " + genderId);
LOGGER.info(" > artistId: " + artistId);
LOGGER.info(" > albumId: " + albumId);
LOGGER.info(" > trackId: " + trackId);
LOGGER.info(" > title: " + title);
LOGGER.info(" > fileInputStream: " + fileInputStream);
LOGGER.info(" > fileMetaData: " + fileMetaData);
/* if (typeId == null) { return Response.status(406). entity("Missong Input 'type'"). type("text/plain"). build(); } */
final long tmpUID = DataTools.getTmpDataId(); final long tmpUID = DataTools.getTmpDataId();
final String sha512 = DataTools.saveTemporaryFile(fileInputStream, tmpUID); final String sha512 = DataTools.saveTemporaryFile(fileInputStream, tmpUID);
Data data = DataTools.getWithSha512(sha512); Data data = DataTools.getWithSha512(db, sha512);
if (data == null) { if (data == null) {
System.out.println("Need to add the data in the BDD ... "); LOGGER.info("Need to add the data in the BDD ... ");
System.out.flush();
try { try {
data = DataTools.createNewData(tmpUID, fileName, sha512); data = DataTools.createNewData(db, tmpUID, fileMetaData.getFileName(), sha512);
} catch (final IOException ex) { } catch (final IOException ex) {
DataTools.removeTemporaryFile(tmpUID); DataTools.removeTemporaryFile(tmpUID);
ex.printStackTrace(); ex.printStackTrace();
@@ -161,80 +177,33 @@ public class TrackResource {
return Response.notModified("Error in SQL insertion ...").build(); return Response.notModified("Error in SQL insertion ...").build();
} }
} else if (data.deleted) { } else if (data.deleted) {
System.out.println("Data already exist but deleted"); LOGGER.info("Data already exist but deleted");
System.out.flush(); DataTools.undelete(db, data.oid);
DataTools.undelete(data.id);
data.deleted = false; data.deleted = false;
} else { } else {
System.out.println("Data already exist ... all good"); LOGGER.info("Data already exist ... all good");
System.out.flush();
} }
// Fist step: retrieve all the Id of each parents:... LOGGER.info("add media");
System.out.println("Find typeNode");
Gender genderElem = null;
if (gender != null) {
genderElem = DataAccess.getWhere(Gender.class, new Condition(new QueryCondition("name", "=", gender)));
if (genderElem == null) {
genderElem = new Gender();
genderElem.name = gender;
genderElem = DataAccess.insert(genderElem);
}
}
// NodeSmall typeNode = TypeResource.getWithId(Long.parseLong(typeId));
// if (typeNode == null) {
// DataTools.removeTemporaryFile(tmpUID);
// return Response.notModified("TypeId does not exist ...").build();
// }
System.out.println(" ==> " + genderElem);
Artist artistElem = null;
if (artist != null) {
artistElem = DataAccess.getWhere(Artist.class, new Condition(new QueryCondition("name", "=", artist)));
if (artistElem == null) {
artistElem = new Artist();
artistElem.name = artist;
artistElem = DataAccess.insert(artistElem);
}
}
System.out.println(" ==> " + artistElem);
Album albumElem = null;
if (album != null) {
albumElem = DataAccess.getWhere(Album.class, new Condition(new QueryCondition("name", "=", album)));
if (albumElem == null) {
albumElem = new Album();
albumElem.name = album;
albumElem = DataAccess.insert(albumElem);
}
}
System.out.println(" ==> " + album);
System.out.println("add media");
Track trackElem = new Track(); Track trackElem = new Track();
trackElem.name = title; trackElem.name = title;
trackElem.track = trackId != null ? Long.parseLong(trackId) : null; trackElem.track = trackId != null ? Long.parseLong(trackId) : null;
trackElem.albumId = albumElem != null ? albumElem.id : null; trackElem.albumId = albumId != null ? Long.parseLong(albumId) : null;
trackElem.genderId = genderElem != null ? genderElem.id : null; trackElem.genderId = genderId != null ? Long.parseLong(genderId) : null;
trackElem.dataId = data.id; trackElem.dataId = data.oid;
// Now list of artis has an internal management: // Now list of artist has an internal management:
if (artistElem != null) { if (artistId != null) {
trackElem.artists = new ArrayList<>(); trackElem.artists = new ArrayList<>();
trackElem.artists.add(artistElem.id); trackElem.artists.add(artistId != null ? Long.parseLong(artistId) : null);
} }
trackElem = DataAccess.insert(trackElem); trackElem = DataAccess.insert(trackElem, new CheckFunction(CHECKER));
/* /* Old mode of artist insertion (removed due to the slowlest request of getting value if (artistElem != null) { this.dam.addLink(Track.class, trackElem.id, "artist", artistElem.id); } */
Old mode of artist insertion (removed due to the slowlest request of getting value
if (artistElem != null) {
DataAccess.addLink(Track.class, trackElem.id, "artist", artistElem.id);
}
*/
return Response.ok(trackElem).build(); return Response.ok(trackElem).build();
} catch (final Exception ex) { } catch (final Exception ex) {
System.out.println("Catch an unexpected error ... " + ex.getMessage()); LOGGER.info("Catch an unexpected error ... {}", ex.getMessage());
ex.printStackTrace(); ex.printStackTrace();
return Response.status(417).entity("Back-end error : " + ex.getMessage()).type("text/plain").build(); return Response.status(417).entity("Back-end error : " + ex.getMessage()).type("text/plain").build();
} }
} }
} }

View File

@@ -1,9 +1,12 @@
package org.kar.karusic.api; package org.kar.karusic.api;
import java.util.List; import java.util.List;
import java.util.Map;
import org.kar.archidata.dataAccess.DataAccess; import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.filter.GenericContext; 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.kar.karusic.model.UserKarusic;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -20,29 +23,29 @@ import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.SecurityContext; import jakarta.ws.rs.core.SecurityContext;
@Path("/users") @Path("/users")
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Produces(MediaType.APPLICATION_JSON)
public class UserResource { public class UserResource {
final Logger logger = LoggerFactory.getLogger(UserResource.class); private static final Logger LOGGER = LoggerFactory.getLogger(UserResource.class);
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
public class UserOut { public class UserOut {
public long id; public long id;
public String login; public String login;
public UserOut(final long id, final String login) { public UserOut(final long id, final String login) {
this.id = id; this.id = id;
this.login = login; this.login = login;
} }
} }
public UserResource() {} public UserResource() {}
// curl http://localhost:9993/api/users // curl http://localhost:9993/api/users
@GET @GET
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
public List<UserKarusic> getUsers() { public List<UserKarusic> gets() {
System.out.println("getUsers"); LOGGER.info("getUsers");
try { try {
return DataAccess.gets(UserKarusic.class); return DataAccess.gets(UserKarusic.class);
} catch (final Exception e) { } catch (final Exception e) {
@@ -51,17 +54,17 @@ public class UserResource {
} }
return null; return null;
} }
// curl http://localhost:9993/api/users/3 // curl http://localhost:9993/api/users/3
@GET @GET
@Path("{id}") @Path("{id}")
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
public UserKarusic getUsers(@Context final SecurityContext sc, @PathParam("id") final long userId) { public UserKarusic get(@Context final SecurityContext sc, @PathParam("id") final long userId) {
System.out.println("getUser " + userId); LOGGER.info("getUser {}", userId);
final GenericContext gc = (GenericContext) sc.getUserPrincipal(); final GenericContext gc = (GenericContext) sc.getUserPrincipal();
System.out.println("==================================================="); LOGGER.info("===================================================");
System.out.println("== USER ? " + gc.userByToken.name); LOGGER.info("== USER {} ", gc.userByToken.name);
System.out.println("==================================================="); LOGGER.info("===================================================");
try { try {
return DataAccess.get(UserKarusic.class, userId); return DataAccess.get(UserKarusic.class, userId);
} catch (final Exception e) { } catch (final Exception e) {
@@ -70,14 +73,19 @@ public class UserResource {
} }
return null; return null;
} }
@GET @GET
@Path("me") @Path("me")
@RolesAllowed("USER") @RolesAllowed("USER")
public UserOut getMe(@Context final SecurityContext sc) { public UserMe getMe(@Context final SecurityContext sc) {
this.logger.debug("getMe()"); LOGGER.debug("getMe()");
final GenericContext gc = (GenericContext) sc.getUserPrincipal(); final GenericContext gc = (GenericContext) sc.getUserPrincipal();
this.logger.debug("== USER ? {}", gc.userByToken); LOGGER.debug("== USER ? {}", gc.userByToken);
return new UserOut(gc.userByToken.id, gc.userByToken.name); return new UserMe(gc.userByToken.id, gc.userByToken.name, //
Map.of(gc.userByToken.name, //
Map.of("admin", PartRight.READ_WRITE, //
"user", PartRight.READ_WRITE), //
"karusic", //
Map.of("user", PartRight.READ)));
} }
} }

View File

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

View File

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

View File

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

View File

@@ -15,9 +15,9 @@ import jakarta.annotation.Priority;
@Priority(Priorities.AUTHENTICATION) @Priority(Priorities.AUTHENTICATION)
public class KarusicAuthenticationFilter extends AuthenticationFilter { public class KarusicAuthenticationFilter extends AuthenticationFilter {
final Logger logger = LoggerFactory.getLogger(KarusicAuthenticationFilter.class); final Logger logger = LoggerFactory.getLogger(KarusicAuthenticationFilter.class);
public KarusicAuthenticationFilter() { public KarusicAuthenticationFilter() {
super("karusic"); super("karusic");
} }
} }

View File

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

View File

@@ -1,5 +1,8 @@
package org.kar.karusic.migration; 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.migration.MigrationSqlStep;
import org.kar.archidata.model.Data; import org.kar.archidata.model.Data;
import org.kar.archidata.model.User; import org.kar.archidata.model.User;
@@ -8,59 +11,59 @@ import org.kar.karusic.model.Artist;
import org.kar.karusic.model.Gender; import org.kar.karusic.model.Gender;
import org.kar.karusic.model.Playlist; import org.kar.karusic.model.Playlist;
import org.kar.karusic.model.Track; import org.kar.karusic.model.Track;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Initialization extends MigrationSqlStep { 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 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 @Override
public String getName() { public String getName() {
return "Initialization"; return "Initialization";
} }
public Initialization() { public Initialization() {
} }
@Override @Override
public void generateStep() throws Exception { public void generateStep() throws Exception {
addClass(Album.class); for (final Class<?> elem : CLASSES_BASE) {
addClass(Artist.class); addClass(elem);
addClass(Data.class); }
addClass(Gender.class);
addClass(Playlist.class); addAction((final DBAccess da) -> {
addClass(Track.class); final List<Gender> data = List.of(//
addClass(User.class); new Gender(1L, "Variété française"), //
new Gender(2L, "Pop"), //
addAction(""" new Gender(3L, "inconnue"), //
INSERT INTO `gender` (`id`, `name`, `description`) VALUES new Gender(4L, "Disco"), //
(1, 'Variété française', NULL), new Gender(5L, "Enfants"), //
(2, 'Pop', NULL), new Gender(6L, "Portugaise"), //
(3, 'inconnue', NULL), new Gender(7L, "Apprentissage"), //
(4, 'Disco', NULL), new Gender(8L, "Blues"), //
(5, 'Enfants', NULL), new Gender(9L, "Jazz"), //
(6, 'Portugaise', NULL), new Gender(10L, "Chanson Noël"), //
(7, 'Apprentissage', NULL), new Gender(11L, "DubStep"), //
(8, 'Blues', NULL), new Gender(12L, "Rap français"), //
(9, 'Jazz', NULL), new Gender(13L, "Classique"), //
(10, 'Chanson Noël', NULL), new Gender(14L, "Rock"), //
(11, 'DubStep', NULL), new Gender(15L, "Electro"), //
(12, 'Rap français', NULL), new Gender(16L, "Celtique"), //
(13, 'Classique', NULL), new Gender(17L, "Country"), //
(14, 'Rock', NULL), new Gender(18L, "Variété Québéquoise"), //
(15, 'Electro', NULL), new Gender(19L, "Médiéval"), //
(16, 'Celtique', NULL), new Gender(20L, "Variété Italienne"), //
(17, 'Country', NULL), new Gender(21L, "Comédie Musicale"), //
(18, 'Variété Québéquoise', NULL), new Gender(22L, "Vianney"), //
(19, 'Médiéval', NULL), new Gender(23L, "Bande Original"), //
(20, 'Variété Italienne', NULL), new Gender(24L, "Bande Originale"), //
(21, 'Comédie Musicale', NULL), new Gender(25L, "Variété Belge"), //
(22, 'Vianney', NULL), new Gender(26L, "Gospel"));
(23, 'Bande Original', NULL), });
(24, 'Bande Originale', NULL),
(25, 'Variété Belge', NULL),
(26, 'Gospel', NULL);
""");
// set start increment element to permit to add after default elements // set start increment element to permit to add after default elements
addAction(""" addAction("""
ALTER TABLE `album` AUTO_INCREMENT = 1000; ALTER TABLE `album` AUTO_INCREMENT = 1000;
@@ -68,9 +71,6 @@ public class Initialization extends MigrationSqlStep {
addAction(""" addAction("""
ALTER TABLE `artist` AUTO_INCREMENT = 1000; ALTER TABLE `artist` AUTO_INCREMENT = 1000;
""", "mysql"); """, "mysql");
addAction("""
ALTER TABLE `data` AUTO_INCREMENT = 1000;
""", "mysql");
addAction(""" addAction("""
ALTER TABLE `gender` AUTO_INCREMENT = 1000; ALTER TABLE `gender` AUTO_INCREMENT = 1000;
""", "mysql"); """, "mysql");
@@ -84,5 +84,26 @@ public class Initialization extends MigrationSqlStep {
ALTER TABLE `user` AUTO_INCREMENT = 1000; ALTER TABLE `user` AUTO_INCREMENT = 1000;
""", "mysql"); """, "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

@@ -11,159 +11,4 @@ public class Migration20231126 extends MigrationSqlStep {
return "migration-2023-11-26: reorder the migration for the new API of archidata"; return "migration-2023-11-26: reorder the migration for the new API of archidata";
} }
public Migration20231126() {
}
@Override
public void generateStep() throws Exception {
// update migration update (last one)
addAction("""
ALTER TABLE `KAR_migration`
CHANGE `id` `id` bigint NOT NULL COMMENT 'Primary key of the base' AUTO_INCREMENT FIRST,
CHANGE `create_date` `createdAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'Create time of the object' AFTER `id`,
CHANGE `modify_date` `updatedAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'When update the object' AFTER `createdAt`,
CHANGE `deleted` `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'When delete, they are not removed, they are just set in a deleted state' AFTER `updatedAt`,
ADD `version` int NOT NULL DEFAULT '2' AFTER `deleted`,
CHANGE `name` `name` varchar(256) COLLATE 'utf8mb4_0900_ai_ci' NULL COMMENT 'Name of the migration' AFTER `version`,
CHANGE `terminated` `terminated` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'if the migration is well terminated or not' AFTER `name`,
CHANGE `stepId` `stepId` int NULL COMMENT 'index in the migration progression' AFTER `terminated`,
CHANGE `count` `count` int NULL COMMENT 'number of element in the migration' AFTER `stepId`,
CHANGE `log` `log` text COLLATE 'utf8mb3_general_ci' NULL COMMENT 'Log generate by the migration' AFTER `count`;
""");
addAction("""
ALTER TABLE `album`
CHANGE `id` `id` bigint NOT NULL COMMENT 'Primary key of the base' AUTO_INCREMENT FIRST,
CHANGE `create_date` `createdAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'Create time of the object' AFTER `id`,
CHANGE `modify_date` `updatedAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'When update the object' AFTER `createdAt`,
CHANGE `deleted` `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'When delete, they are not removed, they are just set in a deleted state' AFTER `updatedAt`,
CHANGE `name` `name` varchar(256) COLLATE 'utf8mb4_0900_ai_ci' NULL AFTER `deleted`,
CHANGE `description` `description` text COLLATE 'utf8mb3_general_ci' NULL AFTER `name`,
CHANGE `publication` `publication` date NULL AFTER `description`;
""");
addAction("""
ALTER TABLE `album_link_cover`
CHANGE `create_date` `createdAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) AFTER `id`,
CHANGE `modify_date` `updatedAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) AFTER `createdAt`,
CHANGE `deleted` `deleted` tinyint(1) NOT NULL DEFAULT '0' AFTER `updatedAt`,
CHANGE `album_id` `object1id` bigint NOT NULL AFTER `deleted`,
CHANGE `cover_id` `object2id` bigint NOT NULL AFTER `object1id`;
""");
addAction("""
ALTER TABLE `artist`
CHANGE `id` `id` bigint NOT NULL COMMENT 'Primary key of the base' AUTO_INCREMENT FIRST,
CHANGE `create_date` `createdAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'Create time of the object' AFTER `id`,
CHANGE `modify_date` `updatedAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'When update the object' AFTER `createdAt`,
CHANGE `deleted` `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'When delete, they are not removed, they are just set in a deleted state' AFTER `updatedAt`,
CHANGE `firstName` `firstName` varchar(256) COLLATE 'utf8mb4_0900_ai_ci' NULL AFTER `description`,
CHANGE `surname` `surname` varchar(256) COLLATE 'utf8mb4_0900_ai_ci' NULL AFTER `firstName`,
CHANGE `birth` `birth` date NULL AFTER `surname`,
CHANGE `death` `death` date NULL AFTER `birth`;
""");
addAction("""
ALTER TABLE `artist_link_cover`
CHANGE `create_date` `createdAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) AFTER `id`,
CHANGE `modify_date` `updatedAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) AFTER `createdAt`,
CHANGE `deleted` `deleted` tinyint(1) NOT NULL DEFAULT '0' AFTER `updatedAt`,
CHANGE `artist_id` `object1id` bigint NOT NULL AFTER `deleted`,
CHANGE `cover_id` `object2id` bigint NOT NULL AFTER `object1id`;
""");
addAction("""
ALTER TABLE `data`
CHANGE `id` `id` bigint NOT NULL COMMENT 'Primary key of the base' AUTO_INCREMENT FIRST,
CHANGE `create_date` `createdAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'Create time of the object' AFTER `id`,
CHANGE `modify_date` `updatedAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'When update the object' AFTER `createdAt`,
CHANGE `deleted` `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'When delete, they are not removed, they are just set in a deleted state' AFTER `updatedAt`,
CHANGE `sha512` `sha512` varchar(128) COLLATE 'utf8mb4_0900_ai_ci' NOT NULL COMMENT 'Sha512 of the data' AFTER `deleted`,
CHANGE `mimeType` `mimeType` varchar(128) COLLATE 'utf8mb4_0900_ai_ci' NOT NULL COMMENT 'Mime -type of the media' AFTER `sha512`,
CHANGE `size` `size` bigint NOT NULL COMMENT 'Size in Byte of the data' AFTER `mimeType`;
""");
addAction("""
ALTER TABLE `gender`
CHANGE `id` `id` bigint NOT NULL COMMENT 'Primary key of the base' AUTO_INCREMENT FIRST,
CHANGE `create_date` `createdAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'Create time of the object' AFTER `id`,
CHANGE `modify_date` `updatedAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'When update the object' AFTER `createdAt`,
CHANGE `name` `name` varchar(256) COLLATE 'utf8mb4_0900_ai_ci' NULL AFTER `deleted`,
CHANGE `description` `description` text COLLATE 'utf8mb3_general_ci' NULL AFTER `name`;
""");
addAction("""
ALTER TABLE `gender_link_cover`
CHANGE `create_date` `createdAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) AFTER `id`,
CHANGE `modify_date` `updatedAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) AFTER `createdAt`,
CHANGE `deleted` `deleted` tinyint(1) NOT NULL DEFAULT '0' AFTER `updatedAt`,
CHANGE `gender_id` `object1id` bigint NOT NULL AFTER `deleted`,
CHANGE `cover_id` `object2id` bigint NOT NULL AFTER `object1id`;
""");
addAction("""
ALTER TABLE `playlist`
CHANGE `id` `id` bigint NOT NULL COMMENT 'Primary key of the base' AUTO_INCREMENT FIRST,
CHANGE `create_date` `createdAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'Create time of the object' AFTER `id`,
CHANGE `modify_date` `updatedAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'When update the object' AFTER `createdAt`,
CHANGE `name` `name` varchar(256) COLLATE 'utf8mb4_0900_ai_ci' NULL AFTER `deleted`,
CHANGE `description` `description` text COLLATE 'utf8mb3_general_ci' NULL AFTER `name`;
""");
addAction("""
ALTER TABLE `playlist_link_cover`
CHANGE `create_date` `createdAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) AFTER `id`,
CHANGE `modify_date` `updatedAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) AFTER `createdAt`,
CHANGE `deleted` `deleted` tinyint(1) NOT NULL DEFAULT '0' AFTER `updatedAt`,
CHANGE `playlist_id` `object1id` bigint NOT NULL AFTER `deleted`,
CHANGE `cover_id` `object2id` bigint NOT NULL AFTER `object1id`;
""");
addAction("""
ALTER TABLE `playlist_link_track`
CHANGE `create_date` `createdAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) AFTER `id`,
CHANGE `modify_date` `updatedAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) AFTER `createdAt`,
CHANGE `deleted` `deleted` tinyint(1) NOT NULL DEFAULT '0' AFTER `updatedAt`,
CHANGE `playlist_id` `object1id` bigint NOT NULL AFTER `deleted`,
CHANGE `track_id` `object2id` bigint NOT NULL AFTER `object1id`;
""");
addAction("""
ALTER TABLE `track`
CHANGE `id` `id` bigint NOT NULL COMMENT 'Primary key of the base' AUTO_INCREMENT FIRST,
CHANGE `create_date` `createdAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'Create time of the object' AFTER `id`,
CHANGE `modify_date` `updatedAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'When update the object' AFTER `createdAt`,
CHANGE `deleted` `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'When delete, they are not removed, they are just set in a deleted state' AFTER `updatedAt`,
CHANGE `name` `name` varchar(256) COLLATE 'utf8mb4_0900_ai_ci' NULL AFTER `deleted`,
CHANGE `description` `description` text COLLATE 'utf8mb3_general_ci' NULL AFTER `name`,
CHANGE `genderId` `genderId` bigint NULL AFTER `description`,
CHANGE `albumId` `albumId` bigint NULL AFTER `genderId`,
CHANGE `track` `track` bigint NULL AFTER `albumId`,
CHANGE `dataId` `dataId` bigint NULL AFTER `track`,
CHANGE `artists` `artists` text COLLATE 'utf8mb4_0900_ai_ci' NULL AFTER `dataId`;
""");
addAction("""
ALTER TABLE `track_link_cover`
CHANGE `create_date` `createdAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) AFTER `id`,
CHANGE `modify_date` `updatedAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) AFTER `createdAt`,
CHANGE `deleted` `deleted` tinyint(1) NOT NULL DEFAULT '0' AFTER `updatedAt`,
CHANGE `track_id` `object1id` bigint NOT NULL AFTER `deleted`,
CHANGE `cover_id` `object2id` bigint NOT NULL AFTER `object1id`;
""");
addAction("""
ALTER TABLE `user`
CHANGE `id` `id` bigint NOT NULL COMMENT 'Primary key of the base' AUTO_INCREMENT FIRST,
CHANGE `create_date` `createdAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'Create time of the object' AFTER `id`,
CHANGE `modify_date` `updatedAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'When update the object' AFTER `createdAt`,
CHANGE `deleted` `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'When delete, they are not removed, they are just set in a deleted state' AFTER `updatedAt`,
CHANGE `login` `login` varchar(128) COLLATE 'utf8mb4_0900_ai_ci' NULL AFTER `deleted`,
CHANGE `lastConnection` `lastConnection` timestamp(3) NULL AFTER `login`,
CHANGE `admin` `admin` tinyint(1) NOT NULL DEFAULT '0' AFTER `lastConnection`,
CHANGE `blocked` `blocked` tinyint(1) NOT NULL DEFAULT '0' AFTER `admin`,
CHANGE `removed` `removed` tinyint(1) NOT NULL DEFAULT '0' AFTER `blocked`;
""");
addAction("""
CREATE TABLE `user_link_cover` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'Primary key of the base' ,
`createdAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'Create time of the object' ,
`updatedAt` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT 'When update the object' ,
`deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'When delete, they are not removed, they are just set in a deleted state' ,
`object1Id` bigint NOT NULL COMMENT 'Object reference 1' ,
`object2Id` bigint NOT NULL COMMENT 'Object reference 2' ,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
""");
}
} }

View File

@@ -0,0 +1,14 @@
package org.kar.karusic.migration;
import org.kar.archidata.migration.MigrationSqlStep;
public class Migration20240225 extends MigrationSqlStep {
public static final int KARSO_INITIALISATION_ID = 1;
@Override
public String getName() {
return "migration-2024-02-25: change model of thrack to use real json";
}
}

View File

@@ -0,0 +1,17 @@
package org.kar.karusic.migration;
import org.kar.archidata.migration.MigrationSqlStep;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Migration20240226 extends MigrationSqlStep {
private static final Logger LOGGER = LoggerFactory.getLogger(Migration20240226.class);
public static final int KARSO_INITIALISATION_ID = 1;
@Override
public String getName() {
return "migration-2024-02-26: convert base with UUID";
}
}

View File

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

View File

@@ -0,0 +1,144 @@
package org.kar.karusic.migration;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.bson.types.ObjectId;
import org.kar.archidata.api.DataResource;
import org.kar.archidata.dataAccess.DBAccess;
import org.kar.archidata.dataAccess.options.AccessDeletedItems;
import org.kar.archidata.dataAccess.options.OverrideTableName;
import org.kar.archidata.migration.MigrationSqlStep;
import org.kar.karusic.migration.model.CoverConversion;
import org.kar.karusic.migration.model.MediaConversion;
import org.kar.karusic.migration.model.OIDConversion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Migration20250104 extends MigrationSqlStep {
private static final Logger LOGGER = LoggerFactory.getLogger(Migration20240226.class);
public static final int KARSO_INITIALISATION_ID = 1;
@Override
public String getName() {
return "migration-2025-01-04: convert base from UUID to OID";
}
@Override
public void generateStep() throws Exception {
// Create a simple function to create objectId in the DB (for manual insertion ...)
// addAction("""
// DELIMITER //
//
// CREATE FUNCTION generate_objectid()
// RETURNS BINARY(12)
// DETERMINISTIC
// BEGIN
// DECLARE ts BINARY(4);
// DECLARE random_part BINARY(5);
// DECLARE counter BINARY(3);
// SET ts = UNHEX(HEX(UNIX_TIMESTAMP()));
// SET random_part = UNHEX(HEX(FLOOR(RAND() * POW(2, 40))));
// SET counter = UNHEX(HEX(FLOOR(RAND() * POW(2, 24))));
// RETURN CONCAT(ts, random_part, counter);
// END //
//
// DELIMITER ;
// """);
addAction("""
ALTER TABLE `data` ADD `_id` binary(12) AFTER `uuid`;
""");
addAction((final DBAccess da) -> {
final List<OIDConversion> datas = da.gets(OIDConversion.class, new AccessDeletedItems(), new OverrideTableName("data"));
for (final OIDConversion elem : datas) {
elem._id = new ObjectId();
}
for (final OIDConversion elem : datas) {
da.update(elem, elem.uuid, List.of("_id"), new OverrideTableName("data"));
}
});
final List<String> tableToTransform = List.of("album", "artist", "gender", "track", "user");
for (final String tableName : tableToTransform) {
addAction("ALTER TABLE `" + tableName + "` ADD `covers_oid` text NULL;");
addAction((final DBAccess da) -> {
final List<OIDConversion> datas = da.gets(OIDConversion.class, new AccessDeletedItems(), new OverrideTableName("data"));
final List<CoverConversion> tableCoverTransforms = da.gets(CoverConversion.class, new AccessDeletedItems(), new OverrideTableName(tableName));
LOGGER.info("Get somes data: {} {}", datas.size(), tableCoverTransforms.size());
for (final CoverConversion tableTransform : tableCoverTransforms) {
final List<ObjectId> values = new ArrayList<>();
if (tableTransform.covers == null) {
continue;
}
for (final UUID link : tableTransform.covers) {
for (final OIDConversion data : datas) {
if (data.uuid.equals(link)) {
values.add(data._id);
break;
}
}
}
if (values.size() != 0) {
tableTransform.covers_oid = values;
LOGGER.info(" update: {}: {} => {}", tableTransform.id, tableTransform.covers, tableTransform.covers_oid);
da.update(tableTransform, tableTransform.id, List.of("covers_oid"), new OverrideTableName(tableName));
}
}
});
addAction("ALTER TABLE `" + tableName + "` DROP `covers`;");
addAction("ALTER TABLE `" + tableName + "` CHANGE `covers_oid` `covers` text NULL;");
}
addAction("""
ALTER TABLE `track` ADD `dataOid` binary(12) AFTER dataId;
""");
addAction((final DBAccess da) -> {
final List<OIDConversion> datas = da.gets(OIDConversion.class, new AccessDeletedItems(), new OverrideTableName("data"));
final List<MediaConversion> medias = da.gets(MediaConversion.class, new AccessDeletedItems(), new OverrideTableName("track"));
for (final MediaConversion media : medias) {
for (final OIDConversion data : datas) {
if (data.uuid.equals(media.dataId)) {
media.dataOid = data._id;
da.update(media, media.id, List.of("dataOid"), new OverrideTableName("track"));
break;
}
}
}
});
addAction("""
ALTER TABLE `track` DROP `dataId`;
""");
addAction("""
ALTER TABLE `track` CHANGE `dataOid` `dataId` binary(12) NOT NULL;
""");
// Move the files...
addAction((final DBAccess da) -> {
final List<OIDConversion> datas = da.gets(OIDConversion.class, new AccessDeletedItems(), new OverrideTableName("data"));
for (final OIDConversion data : datas) {
final String origin = DataResource.getFileDataOld(data.uuid);
final String destination = DataResource.getFileData(data._id);
LOGGER.info("move file = {}", origin);
LOGGER.info(" ==> {}", destination);
try {
Files.move(Paths.get(origin), Paths.get(destination), StandardCopyOption.ATOMIC_MOVE);
} catch (final NoSuchFileException ex) {
LOGGER.warn("Fail to move file : {}", ex.getMessage());
}
}
});
addAction("""
ALTER TABLE `data` DROP `uuid`;
""");
// addAction("""
// ALTER TABLE `data` CHANGE `_id` `_id` BINARY(12) DEFAULT (generate_objectid());
// """);
addAction("""
ALTER TABLE `data` ADD PRIMARY KEY `_id` (`_id`);
""");
}
}

View File

@@ -0,0 +1,18 @@
package org.kar.karusic.migration.model;
import java.util.List;
import java.util.UUID;
import org.bson.types.ObjectId;
import org.kar.archidata.annotation.DataJson;
import jakarta.persistence.Id;
public class CoverConversion {
@Id
public Long id = null;
@DataJson
public List<UUID> covers = null;
@DataJson
public List<ObjectId> covers_oid = null;
}

View File

@@ -0,0 +1,14 @@
package org.kar.karusic.migration.model;
import java.util.UUID;
import org.bson.types.ObjectId;
import jakarta.persistence.Id;
public class MediaConversion {
@Id
public Long id = null;
public UUID dataId = null;
public ObjectId dataOid = null;
}

View File

@@ -0,0 +1,13 @@
package org.kar.karusic.migration.model;
import java.util.UUID;
import org.bson.types.ObjectId;
import jakarta.persistence.Id;
public class OIDConversion {
@Id
public UUID uuid = null;
public ObjectId _id = null;
}

View File

@@ -1,16 +1,41 @@
package org.kar.karusic.model; package org.kar.karusic.model;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List;
import org.bson.types.ObjectId;
import org.kar.archidata.annotation.DataIfNotExists; 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 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; import jakarta.persistence.Table;
@Entity("Album")
@Table(name = "album") @Table(name = "album")
@DataIfNotExists @DataIfNotExists
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
public class Album extends NodeSmall { public class Album extends GenericDataSoftDelete {
public static class AlbumChecker extends CheckJPA<Album> {
public AlbumChecker() {
super(Album.class);
}
}
@Column(length = 256)
public String name = null;
@Column(length = 0)
public String description = null;
@Schema(description = "List of Id of the specific covers")
@DataJson(targetEntity = Data.class)
@Nullable
public List<ObjectId> covers = null;
public LocalDate publication = null; public LocalDate publication = null;
} }

View File

@@ -1,18 +1,42 @@
package org.kar.karusic.model; package org.kar.karusic.model;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List;
import org.bson.types.ObjectId;
import org.kar.archidata.annotation.DataIfNotExists; 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 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.Column;
import jakarta.persistence.Table; import jakarta.persistence.Table;
@Entity("Artist")
@Table(name = "artist") @Table(name = "artist")
@DataIfNotExists @DataIfNotExists
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
public class Artist extends NodeSmall { public class Artist extends GenericDataSoftDelete {
public static class ArtistChecker extends CheckJPA<Artist> {
public ArtistChecker() {
super(Artist.class);
}
}
@Column(length = 256)
public String name = null;
@Column(length = 0)
public String description = null;
@Schema(description = "List of Id of the specific covers")
@DataJson(targetEntity = Data.class)
@Nullable
public List<ObjectId> covers = null;
@Column(length = 256) @Column(length = 256)
public String firstName = null; public String firstName = null;
@Column(length = 256) @Column(length = 256)

View File

@@ -1,55 +0,0 @@
package org.kar.karusic.model;
/*
CREATE TABLE `data` (
`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',
`sha512` varchar(129) COLLATE 'utf8_general_ci' NOT NULL,
`mime_type` varchar(128) COLLATE 'utf8_general_ci' NOT NULL,
`size` bigint,
`original_name` TEXT
) AUTO_INCREMENT=64;
*/
import java.sql.ResultSet;
import java.sql.SQLException;
public class DataSmall {
public Long id;
public String sha512;
public String mimeType;
public Long size;
public DataSmall() {
}
public DataSmall(ResultSet rs) {
int iii = 1;
try {
this.id = rs.getLong(iii++);
this.sha512 = rs.getString(iii++);
this.mimeType = rs.getString(iii++);
this.size = rs.getLong(iii++);
} catch (SQLException ex) {
ex.printStackTrace();
}
}
public String getTableSql() {
return """
DROP TABLE IF EXISTS `data`;
CREATE TABLE `data` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'table ID',
`deleted` tinyint(1) NOT NULL DEFAULT '0',
`create_date` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'Time the element has been created',
`modify_date` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'Time the element has been update',
`sha512` varchar(129) CHARACTER SET utf8mb3 COLLATE utf8_general_ci NOT NULL,
`mime_type` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8_general_ci NOT NULL,
`size` bigint DEFAULT NULL,
`original_name` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
""";
}
}

View File

@@ -12,15 +12,48 @@ CREATE TABLE `node` (
) AUTO_INCREMENT=10; ) AUTO_INCREMENT=10;
*/ */
import java.util.List;
import org.bson.types.ObjectId;
import org.kar.archidata.annotation.DataIfNotExists; 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 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; import jakarta.persistence.Table;
@Entity("Gender")
@Table(name = "gender") @Table(name = "gender")
@DataIfNotExists @DataIfNotExists
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
public class Gender extends NodeSmall { public class Gender extends GenericDataSoftDelete {
public static class GenderChecker extends CheckJPA<Gender> {
public GenderChecker() {
super(Gender.class);
}
}
@Column(length = 256)
public String name = null;
@Column(length = 0)
public String description = null;
@Schema(description = "List of Id of the specific covers")
@DataJson(targetEntity = Data.class)
@Nullable
public List<ObjectId> covers = null;
public Gender() {}
public Gender(final Long id, final String name) {
this.id = id;
this.name = name;
}
} }

View File

@@ -1,104 +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", "UNIVERSE", "SERIES", "SEASON") 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.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class MediaSmall {
public class MediaStreamProperty {
public Long id;
public Long timeSecond;
public Long width;
public Long height;
public Map<String, Long> videos = new HashMap<>();
public Map<String, Long> audios = new HashMap<>();
public Map<String, Long> subtitles = new HashMap<>();
}
public Long id;
public String name;
public String description;
public Long dataId;
public Long typeId;
public Long universeId;
public Long seriesId;
public Long seasonId;
public Integer episode;
public Integer date;
public Integer time;
public String ageLimit;
public List<Long> covers = null;
public MediaStreamProperty media;
public MediaSmall(ResultSet rs) {
int iii = 1;
try {
this.id = rs.getLong(iii++);
this.name = rs.getString(iii++);
this.description = rs.getString(iii++);
this.dataId = rs.getLong(iii++);
if (rs.wasNull()) {
this.dataId = null;
}
this.typeId = rs.getLong(iii++);
if (rs.wasNull()) {
this.typeId = null;
}
this.universeId = rs.getLong(iii++);
if (rs.wasNull()) {
this.universeId = null;
}
this.seriesId = rs.getLong(iii++);
if (rs.wasNull()) {
this.seriesId = null;
}
this.seasonId = rs.getLong(iii++);
if (rs.wasNull()) {
this.seasonId = null;
}
this.episode = rs.getInt(iii++);
if (rs.wasNull()) {
this.episode = null;
}
this.date = rs.getInt(iii++);
if (rs.wasNull()) {
this.date = null;
}
this.time = rs.getInt(iii++);
if (rs.wasNull()) {
this.time = null;
}
this.ageLimit = rs.getString(iii++);
String coversString = rs.getString(iii++);
if (!rs.wasNull()) {
covers = new ArrayList<>();
String[] elements = coversString.split("-");
for (String elem : elements) {
Long tmp = Long.parseLong(elem);
covers.add(tmp);
}
}
} catch (SQLException ex) {
ex.printStackTrace();
}
}
}

View File

@@ -1,34 +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.kar.archidata.model.Data;
import org.kar.archidata.model.GenericDataSoftDelete;
import com.fasterxml.jackson.annotation.JsonInclude;
import jakarta.persistence.Column;
import jakarta.persistence.FetchType;
import jakarta.persistence.ManyToMany;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class NodeSmall extends GenericDataSoftDelete {
@Column(length = 256)
public String name = null;
@Column(length = 0)
public String description = null;
@ManyToMany(fetch = FetchType.LAZY, targetEntity = Data.class)
public List<Long> covers = null;
}

View File

@@ -14,18 +14,42 @@ CREATE TABLE `node` (
import java.util.List; import java.util.List;
import org.bson.types.ObjectId;
import org.kar.archidata.annotation.DataIfNotExists; 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 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.FetchType;
import jakarta.persistence.ManyToMany; import jakarta.persistence.ManyToMany;
import jakarta.persistence.Table; import jakarta.persistence.Table;
@Entity("Playlist")
@Table(name = "playlist") @Table(name = "playlist")
@DataIfNotExists @DataIfNotExists
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
public class Playlist extends NodeSmall { public class Playlist extends GenericDataSoftDelete {
public static class PlaylistChecker extends CheckJPA<Playlist> {
public PlaylistChecker() {
super(Playlist.class);
}
}
@Column(length = 256)
public String name = null;
@Column(length = 0)
public String description = null;
@Schema(description = "List of Id of the specific covers")
@DataJson(targetEntity = Data.class)
@Nullable
public List<ObjectId> covers = null;
@ManyToMany(fetch = FetchType.LAZY, targetEntity = Track.class) @ManyToMany(fetch = FetchType.LAZY, targetEntity = Track.class)
public List<Long> tracks = null; public List<ObjectId> tracks = null;
} }

View File

@@ -1,10 +1,10 @@
package org.kar.karusic.model; package org.kar.karusic.model;
public enum State { public enum State {
// User has remove his account // User has remove his account
REMOVED, REMOVED,
// User has been blocked his account // User has been blocked his account
BLOCKED, BLOCKED,
// generic user // generic user
USER USER
} }

View File

@@ -10,31 +10,54 @@ CREATE TABLE `node` (
`description` TEXT COLLATE 'utf8_general_ci', `description` TEXT COLLATE 'utf8_general_ci',
`parent_id` bigint `parent_id` bigint
) AUTO_INCREMENT=10; ) AUTO_INCREMENT=10;
*/ */
import java.util.List; import java.util.List;
import org.bson.types.ObjectId;
import org.kar.archidata.annotation.DataIfNotExists; import org.kar.archidata.annotation.DataIfNotExists;
import org.kar.archidata.annotation.addOn.SQLTableExternalForeinKeyAsList; 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 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.Column;
import jakarta.persistence.Table; import jakarta.persistence.Table;
@Entity("Track")
@Table(name = "track") @Table(name = "track")
@DataIfNotExists @DataIfNotExists
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
public class Track extends NodeSmall { public class Track extends GenericDataSoftDelete {
public static class TrackChecker extends CheckJPA<Track> {
public TrackChecker() {
super(Track.class);
}
}
@Column(length = 256)
public String name = null;
@Column(length = 0)
public String description = null;
@Schema(description = "List of Id of the specific covers")
@DataJson(targetEntity = Data.class)
@Nullable
public List<ObjectId> covers = null;
public Long genderId = null; public Long genderId = null;
public Long albumId = null; public Long albumId = null;
public Long track = null; public Long track = null;
public Long dataId = null; public ObjectId dataId = null;
//@ManyToMany(fetch = FetchType.LAZY, targetEntity = Artist.class) // @ManyToMany(fetch = FetchType.LAZY, targetEntity = Artist.class)
@SQLTableExternalForeinKeyAsList @DataJson
@Column(length = 0) @Column(length = 0)
public List<Long> artists = null; public List<Long> artists = null;
@Override @Override
public String toString() { public String toString() {
return "Track [id=" + this.id + ", deleted=" + this.deleted + ", createdAt=" + this.createdAt + ", updatedAt=" + this.updatedAt + ", name=" + this.name + ", description=" + this.description return "Track [id=" + this.id + ", deleted=" + this.deleted + ", createdAt=" + this.createdAt + ", updatedAt=" + this.updatedAt + ", name=" + this.name + ", description=" + this.description

View File

@@ -3,13 +3,12 @@ package org.kar.karusic.util;
public class ConfigVariable { public class ConfigVariable {
public static final String BASE_NAME = "ORG_KARUSIC_"; public static final String BASE_NAME = "ORG_KARUSIC_";
public static String getFrontFolder() {
public static String getFrontFolder() { String out = System.getenv(BASE_NAME + "FRONT_FOLDER");
String out = System.getenv(BASE_NAME + "FRONT_FOLDER"); if (out == null) {
if (out == null) { return "/application/front";
return "/application/front"; }
} return out;
return out; }
}
} }

View File

@@ -0,0 +1,18 @@
<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>

View File

@@ -3,12 +3,14 @@
# Default logging detail level for all instances of SimpleLogger. # Default logging detail level for all instances of SimpleLogger.
# Must be one of ("trace", "debug", "info", "warn", or "error"). # Must be one of ("trace", "debug", "info", "warn", or "error").
# If not specified, defaults to "info". # If not specified, defaults to "info".
org.slf4j.simpleLogger.defaultLogLevel=trace org.slf4j.simpleLogger.defaultLogLevel=INFO
# Logging detail level for a SimpleLogger instance named "xxxxx". # Logging detail level for a SimpleLogger instance named "xxxxx".
# Must be one of ("trace", "debug", "info", "warn", or "error"). # Must be one of ("trace", "debug", "info", "warn", or "error").
# If not specified, the default logging detail level is used. # If not specified, the default logging detail level is used.
#org.slf4j.simpleLogger.log.xxxxx= #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. # 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. # Default is false, and will output the number of milliseconds elapsed since startup.
@@ -32,4 +34,9 @@ org.slf4j.simpleLogger.showThreadName=true
# Defaults to false. # Defaults to false.
#org.slf4j.simpleLogger.showShortLogName=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

View File

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

View File

@@ -0,0 +1,126 @@
package test.kar.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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.InternalServerErrorException;
public class ConfigureDb {
final static private Logger LOGGER = LoggerFactory.getLogger(ConfigureDb.class);
final static private String modeTestForced = null;// "MONGO";
public static DBAccess da = null;
public static void configure() throws IOException, InternalServerErrorException, DataAccessException {
String modeTest = System.getenv("TEST_E2E_MODE");
if (modeTest == null || modeTest.isEmpty() || "false".equalsIgnoreCase(modeTest)) {
modeTest = "SQLITE-MEMORY";
} else if ("true".equalsIgnoreCase(modeTest)) {
modeTest = "MY-SQL";
}
// override the local test:
if (modeTestForced != null) {
modeTest = modeTestForced;
}
final List<Class<?>> listObject = List.of( //
Album.class, //
Artist.class, //
Gender.class, //
Playlist.class, //
Track.class, //
UserKarusic.class //
);
if ("SQLITE-MEMORY".equalsIgnoreCase(modeTest)) {
ConfigBaseVariable.dbType = "sqlite";
ConfigBaseVariable.bdDatabase = null;
ConfigBaseVariable.dbHost = "memory";
// for test we need to connect all time the DB
ConfigBaseVariable.dbKeepConnected = "true";
} else if ("SQLITE".equalsIgnoreCase(modeTest)) {
ConfigBaseVariable.dbType = "sqlite";
ConfigBaseVariable.bdDatabase = null;
ConfigBaseVariable.dbKeepConnected = "true";
} else if ("MY-SQL".equalsIgnoreCase(modeTest)) {
ConfigBaseVariable.dbType = "mysql";
ConfigBaseVariable.bdDatabase = "test_karusic_db";
ConfigBaseVariable.dbPort = "3906";
ConfigBaseVariable.dbUser = "root";
} else if ("MONGO".equalsIgnoreCase(modeTest)) {
ConfigBaseVariable.dbType = "mongo";
ConfigBaseVariable.bdDatabase = "test_karusic_db";
} else {
// User local modification ...
ConfigBaseVariable.bdDatabase = "test_karusic_db";
ConfigBaseVariable.dbPort = "3906";
ConfigBaseVariable.dbUser = "root";
}
removeDB();
// Connect the dataBase...
da = DBAccess.createInterface();
}
public static void removeDB() {
String modeTest = System.getenv("TEST_E2E_MODE");
if (modeTest == null || modeTest.isEmpty() || "false".equalsIgnoreCase(modeTest)) {
modeTest = "SQLITE-MEMORY";
} else if ("true".equalsIgnoreCase(modeTest)) {
modeTest = "MY-SQL";
}
// override the local test:
if (modeTestForced != null) {
modeTest = modeTestForced;
}
DbConfig config = null;
try {
config = new DbConfig();
} catch (final DataAccessException e) {
e.printStackTrace();
LOGGER.error("Fail to clean the DB");
return;
}
config.setDbName(null);
LOGGER.info("Remove the DB and create a new one '{}'", config.getDbName());
try (final DBAccess daRoot = DBAccess.createInterface(config)) {
if ("SQLITE-MEMORY".equalsIgnoreCase(modeTest)) {
// nothing to do ...
} else if ("SQLITE".equalsIgnoreCase(modeTest)) {
daRoot.deleteDB(ConfigBaseVariable.bdDatabase);
} else if ("MY-SQL".equalsIgnoreCase(modeTest)) {
daRoot.deleteDB(ConfigBaseVariable.bdDatabase);
} else if ("MONGO".equalsIgnoreCase(modeTest)) {
daRoot.deleteDB(ConfigBaseVariable.bdDatabase);
}
daRoot.createDB(ConfigBaseVariable.bdDatabase);
} catch (final InternalServerErrorException e) {
e.printStackTrace();
LOGGER.error("Fail to clean the DB");
return;
} catch (final IOException e) {
e.printStackTrace();
LOGGER.error("Fail to clean the DB");
return;
}
}
public static void clear() throws IOException {
LOGGER.info("Remove the test db");
removeDB();
// The connection is by default open ==> close it at the end of test:
da.close();
DbIoFactory.closeAllForceMode();
ConfigBaseVariable.clearAllValue();
}
}

View File

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

View File

@@ -1,28 +1,12 @@
package test.kar.karusic; package test.kar.karusic;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.kar.karusic.WebLauncher; import org.kar.karusic.WebLauncher;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public class WebLauncherTest extends WebLauncher { public class WebLauncherTest extends WebLauncher {
final private static Logger LOGGER = LoggerFactory.getLogger(WebLauncherTest.class); final private static Logger LOGGER = LoggerFactory.getLogger(WebLauncherTest.class);
public WebLauncherTest() {
LOGGER.debug("Configure REST system");
// for local test:
ConfigBaseVariable.apiAdress = "http://127.0.0.1:12345/test/api/";
ConfigBaseVariable.dbPort = "3306"; public WebLauncherTest() {}
// for the test we a in memory sqlite..
ConfigBaseVariable.dbType = "sqlite";
ConfigBaseVariable.dbHost = "memory";
// for test we need to connect all time the DB
ConfigBaseVariable.dbKeepConnected = "true";
ConfigBaseVariable.dbHost = "localhost";
ConfigBaseVariable.dbUser = "root";
ConfigBaseVariable.dbPassword = "ZERTYSDGFVHSDFGHJYZSDFGSQxfgsqdfgsqdrf4564654";
}
} }

View File

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

View File

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

View File

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

2
front/.env.production Normal file
View File

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

View File

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

View File

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

3
front/.gitignore vendored
View File

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

27
front/.storybook/main.ts Normal file
View File

@@ -0,0 +1,27 @@
import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
framework: {
name: '@storybook/react-vite',
options: {},
},
core: {
disableTelemetry: true,
builder: '@storybook/builder-vite',
},
stories: ['../src/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
staticDirs: ['../public'],
typescript: {
reactDocgen: false,
},
docs: {},
};
export default config;

View File

@@ -0,0 +1,16 @@
<style>
html {
background: transparent !important;
}
.docs-story > :first-child {
padding: 0;
}
.docs-story > * {
background: transparent !important;
}
#root #start-ui-storybook-wrapper {
min-height: 100vh;
}
</style>

View File

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

View File

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

View File

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

2
front/LICENSE Normal file
View File

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

View File

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

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

@@ -0,0 +1,6 @@
{
"display": "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"
}

25
front/build.js Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

13
front/index.html Normal file
View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Karusic</title>
<link rel="icon" href="/favicon.ico" />
</head>
<body style="width:100vw;height:100vh;min-width:100%;min-height:100%;">
<div id="root" style="width:100%;height:100%;min-width:100%;min-height:100%;"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

View File

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

18
front/knip.ts Normal file
View File

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

21733
front/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,40 +1,111 @@
{ {
"name": "karusic", "name": "karusic",
"version": "0.0.0",
"license": "MIT",
"scripts": {
"all": "npm run build && npm run test",
"ng": "ng",
"start": "ng serve --configuration=develop --watch --port 4203",
"build": "ng build --prod",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true, "private": true,
"version": "0.0.1",
"description": "KAR web music application",
"author": {
"name": "Edouard DUPIN",
"email": "yui.heero@gmail.farm"
},
"license": "PROPRIETARY",
"engines": {
"node": ">=20"
},
"scripts": {
"update_packages": "ncu --upgrade",
"install_dependency": "pnpm install",
"test": "vitest run",
"test:watch": "vitest watch",
"build": "tsc && vite build",
"static:build": "node build.js && pnpm build",
"dev": "vite",
"pretty": "prettier -w .",
"lint": "pnpm tsc --noEmit",
"storybook": "storybook dev -p 3001",
"storybook:build": "storybook build && mv ./storybook-static ./public/storybook"
},
"lint-staged": {
"*.{ts,tsx,js,jsx,json}": "prettier --write"
},
"dependencies": { "dependencies": {
"@angular/animations": "^14.2.10", "@chakra-ui/anatomy": "2.2.2",
"@angular/cdk": "^14.2.7", "@chakra-ui/cli": "2.4.1",
"@angular/common": "^14.2.10", "@chakra-ui/react": "2.8.2",
"@angular/compiler": "^14.2.10", "@chakra-ui/theme-tools": "2.2.6",
"@angular/core": "^14.2.10", "@dnd-kit/core": "6.3.1",
"@angular/forms": "^14.2.10", "@dnd-kit/modifiers": "9.0.0",
"@angular/material": "^14.2.7", "@dnd-kit/sortable": "10.0.0",
"@angular/platform-browser": "^14.2.10", "@dnd-kit/utilities": "3.2.2",
"@angular/platform-browser-dynamic": "^14.2.10", "@emotion/react": "11.14.0",
"@angular/router": "^14.2.10", "@emotion/styled": "11.14.0",
"rxjs": "^7.5.7", "@formiz/core": "2.4.5",
"zone.js": "^0.12.0" "@formiz/validations": "2.0.1",
"allotment": "1.20.2",
"css-mediaquery": "0.1.2",
"dayjs": "1.11.13",
"history": "5.3.0",
"react": "18.3.1",
"react-color-palette": "7.3.0",
"react-currency-input-field": "3.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",
"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"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^14.2.9", "@chakra-ui/styled-system": "2.12.0",
"@angular-eslint/builder": "14.2.0", "@playwright/test": "1.49.1",
"@angular-eslint/eslint-plugin": "14.2.0", "@storybook/addon-actions": "8.4.7",
"@angular-eslint/eslint-plugin-template": "14.2.0", "@storybook/addon-essentials": "8.4.7",
"@angular-eslint/schematics": "14.2.0", "@storybook/addon-links": "8.4.7",
"@angular-eslint/template-parser": "14.2.0", "@storybook/addon-mdx-gfm": "8.4.7",
"@angular/cli": "^14.2.9", "@storybook/react": "8.4.7",
"@angular/compiler-cli": "^14.2.10", "@storybook/react-vite": "8.4.7",
"@angular/language-service": "^14.2.10" "@storybook/theming": "8.4.7",
"@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",
"@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",
"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",
"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",
"ts-node": "10.9.2",
"typescript": "5.7.2",
"vite": "6.0.7",
"vitest": "2.1.8"
} }
} }

11777
front/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

16
front/prettier.config.js Normal file
View File

@@ -0,0 +1,16 @@
// Using a JS file, allowing us to add comments
module.exports = {
// This plugins line is mandatory for the plugin to work with pnpm.
// https://github.com/trivago/prettier-plugin-sort-imports/blob/61d069711008c530f5a41ca4e254781abc5de358/README.md?plain=1#L89-L96
plugins: ['@trivago/prettier-plugin-sort-imports'],
endOfLine: 'lf',
semi: true,
singleQuote: true,
tabWidth: 2,
trailingComma: 'es5',
arrowParens: 'always',
importOrder: ['^react$', '^(?!^react$|^@/|^[./]).*', '^@/(.*)$', '^[./]'],
importOrderSeparation: true,
importOrderSortSpecifiers: true,
importOrderParserPlugins: ['jsx', 'typescript'],
};

View File

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

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

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

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

@@ -0,0 +1,118 @@
import { useState } from 'react';
import { ChakraProvider, Select } from '@chakra-ui/react';
import {
Box,
Button,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Stack,
Text,
useDisclosure,
} from '@chakra-ui/react';
import { environment } from '@/environment';
import { App as SpaApp } from '@/scene/App';
import { USERS } from '@/service/session';
import theme from '@/theme';
import { hashLocalData } from '@/utils/sso';
const AppEnvHint = () => {
const modal = useDisclosure();
const [selectUserTest, setSelectUserTest] = useState<string>('NO_USER');
//const setUser = useRightsStore((store) => store.setUser);
const buildEnv =
process.env.NODE_ENV === 'development'
? 'Development'
: import.meta.env.VITE_DEV_ENV_NAME;
const envName: Array<string> = [];
!!buildEnv && envName.push(buildEnv);
if (!envName.length) {
return null;
}
const handleChange = (selectedOption) => {
console.log(`SELECT: [${selectedOption.target.value}]`);
setSelectUserTest(selectedOption.target.value);
};
const onClose = () => {
modal.onClose();
if (selectUserTest == 'NO_USER') {
window.location.href = `/${environment.applName}/sso/${hashLocalData()}/false/__LOGOUT__`;
} else {
window.location.href = `/${environment.applName}/sso/${hashLocalData()}/true/${USERS[selectUserTest]}`;
}
};
return (
<>
<Box
zIndex="100000"
position="fixed"
top="0"
insetStart="0"
insetEnd="0"
h="2px"
bg="warning.400"
as="button"
cursor="pointer"
data-test-id="devtools"
onClick={modal.onOpen}
>
<Text
position="fixed"
top="0"
insetStart="4"
bg="warning.400"
color="warning.900"
fontSize="0.6rem"
fontWeight="bold"
px="10px"
marginLeft="25%"
borderBottomStartRadius="sm"
borderBottomEndRadius="sm"
textTransform="uppercase"
>
{envName.join(' : ')}
</Text>
</Box>
<Modal isOpen={modal.isOpen} onClose={modal.onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Outils développeurs</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Stack>
<Text>Utilisateur</Text>
<Select placeholder="Select test user" onChange={handleChange}>
{Object.keys(USERS).map((key) => (
<option value={key} key={key}>
{key}
</option>
))}
</Select>
</Stack>
</ModalBody>
<ModalFooter>
<Button onClick={onClose}>Apply</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
const App = () => {
return (
<ChakraProvider theme={theme}>
<AppEnvHint />
<SpaApp />
</ChakraProvider>
);
};
export default App;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,46 +0,0 @@
<div class="bottom" *ngIf="mediaSource">
<div class="media" >
<div class="media-elem">
<audio width="1" height="1" src="{{mediaSource}}"
#mediaPlayer
preload
(play)="changeStateToPlay()"
(pause)="changeStateToPause()"
(timeupdate)="changeTimeupdate($event.currentTime)"
(durationchange)="changeDurationchange($event.duration)"
(loadedmetadata)="changeMetadata()"
autoplay
(ended)="onAudioEnded()"
><!-- controls > --> <!--preload="none"-->
<!--<p>Your browser does not support HTML5 media player. download media: <a href="{{mediaSource}}>link here</a>.</p>-->
</audio>
</div>
<div class="controls">
<div class="timer">
<div class="timer-title">
<label class="unselectable label">{{nameData}}</label>
</div>
<div class="timer-lines">
<input type="range" min="0" class="slider"
[value]="currentTime"
[max]="duration"
(input)="seek($event.target)">
</div>
<div class="timer-text">
<label class="unselectable label">{{currentTimeDisplay}} / {{durationDisplay}}</label>
</div>
<div class="timer-control">
<button (click)="onPlay()" *ngIf="!isPlaying" ><i class="material-icons">play_arrow</i></button>
<button (click)="onPause()" *ngIf="isPlaying" ><i class="material-icons">pause</i></button>
<button (click)="onStop()" ><i class="material-icons">stop</i></button>
<button [disabled]="havePrevious" (click)="onBefore()"><i class="material-icons">navigate_before</i></button>
<button (click)="onRewind()"><i class="material-icons">fast_rewind</i></button>
<button (click)="onForward()"><i class="material-icons">fast_forward</i></button>
<button [disabled]="haveNext" (click)="onNext()"><i class="material-icons">navigate_next</i></button>
<button (click)="onPlayMode()"><i class="material-icons">{{playMode}}</i></button>
<!--<button (click)="onMore()" ><i class="material-icons">more_vert</i></button>-->
<!--<button (click)="onTakeScreenShoot()"><i class="material-icons">add_a_photo</i></button>-->
</div>
</div>
</div>
</div>

View File

@@ -1,159 +0,0 @@
.bottom {
.controls {
//visibility: hidden;
opacity: 0.85;
width: 96%;
height: 150px;
border-radius: 10px;
position: absolute;
bottom: 20px;
left: 2%;
//margin-left: -200px;
background-color: black;
box-shadow: 3px 3px 5px black;
transition: 1s all;
display: flex;
font-size: 40px;
.material-icons {
color: #FFF;
font-size: 40px;
}
button {
border: none;
background: none;
}
button:disabled,
button[disabled] {
.material-icons {
color: rgb(46, 46, 46);
}
}
.slidecontainer {
line-height: 38px;
font-size: 10px;
font-family: monospace;
text-shadow: 1px 1px 0px black;
color: white;
flex: 5;
position: relative;
}
.slider {
position: relative;
-webkit-appearance: none;
//width: calc(100% - 60px);
width: 95%;
height: 10px;
top: 5px;
border-radius: 5px;
background: #d3d3d3;
outline: none;
opacity: 0.7;
-webkit-transition: .2s;
transition: opacity .2s;
flex: 5;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 25px;
height: 25px;
border-radius: 50%;
background: #4CAF50;
cursor: pointer;
}
.slider::-moz-range-thumb {
width: 25px;
height: 25px;
border-radius: 50%;
background: #4CAF50;
cursor: pointer;
}
.timer-title {
position: absolute;
left: 30px;
width: 100%;
font-size: 20px;
font-weight:bold;
bottom: 104px;
}
.timer-lines {
position: absolute;
left: 30px;
//width: calc(100%-60px);
font-size: 20px;
font-weight:bold;
bottom: 82px;
}
.timer-text {
position: absolute;
left: 30px;
width: 100%;
font-size: 20px;
font-weight:bold;
bottom: 40px;
}
.timer-control {
position: absolute;
//top: 25px;
left: 0px;
width: 100%;
//line-height: 38px;
font-size: 30px;
font-weight:bold;
bottom: 0px;
}
.label {
//transform: translate(-12px,-12px);
vertical-align: text-top;
line-height: 18px;
min-width: 50%;
//display: block,
}
}
/*
:hover .controls, :focus .controls {
opacity: 1;
}
*/
button:before {
font-family: HeydingsControlsRegular;
font-size: 30px;
position: relative;
color: #FFF;
//text-shadow: 1px 1px 0px black;
}
.timer {
line-height: 38px;
font-size: 30px;
font-family: monospace;
text-shadow: 1px 1px 0px black;
color: white;
flex: 5;
position: relative;
}
.timer div {
width:100%;
line-height: 38px;
font-size: 10px;
font-family: monospace;
text-shadow: 1px 1px 0px black;
color: white;
flex: 5;
position: relative;
}
.timer span {
position: absolute;
z-index: 3;
left: 19px;
}
}

View File

@@ -1,501 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2018, Edouard DUPIN, all right reserved
* @license PROPRIETARY (see license file)
*/
import { Component, OnInit, Input, ViewChild, ElementRef } from '@angular/core';
import { isArray, isArrayOf, isNullOrUndefined, isNumberFinite } from 'common/utils';
import { NodeData } from 'common/model';
import { GenderService, DataService, PlayerService, TrackService, AlbumService, ArtistService } from 'app/service';
import { PlaylistCurrent } from 'app/service/player';
import { Media } from 'app/model';
import { HttpWrapperService } from 'common/service';
import { Title } from '@angular/platform-browser';
import { environment } from 'environments/environment';
export enum PlayMode {
PLAY_ONE = "check",
PLAY_ALL = "trending_flat",
PLAY_ONE_LOOP = "repeat_one",
PLAY_ALL_LOOP = "repeat",
};
// note: all the input is managed with the player service ...
@Component({
selector: 'app-element-player-audio',
templateUrl: './element-player-audio.html',
styleUrls: [ './element-player-audio.less' ]
})
export class ElementPlayerAudioComponent implements OnInit {
mediaPlayer: HTMLAudioElement;
duration: number;
durationDisplay: string;
@ViewChild('mediaPlayer')
set mainVideoEl(el: ElementRef) {
if(el !== null && el !== undefined) {
this.mediaPlayer = el.nativeElement;
}
}
public mediaSource: string = undefined;
public name: string = 'rr';
public description: string;
public counttrack: number;
public covers: string[];
public volume_value: number = 100;
public volume_displayMenu: boolean = false;
nameData: string;
playStream: boolean;
isPlaying: boolean;
currentTime: any;
currentTimeDisplay: string;
havePrevious: boolean = false;
haveNext: boolean = false;
playMode: PlayMode = PlayMode.PLAY_ALL;
onPlayMode() {
if (this.playMode === PlayMode.PLAY_ONE) {
this.playMode = PlayMode.PLAY_ALL;
} else if (this.playMode === PlayMode.PLAY_ALL) {
this.playMode = PlayMode.PLAY_ONE_LOOP;
} else if (this.playMode === PlayMode.PLAY_ONE_LOOP) {
this.playMode = PlayMode.PLAY_ALL_LOOP;
} else {
this.playMode = PlayMode.PLAY_ONE;
}
}
constructor(
private playerService: PlayerService,
private trackService: TrackService,
private albumService: AlbumService,
private artistService: ArtistService,
private httpService: HttpWrapperService,
private dataService: DataService,
private titleService: Title) {
// nothing to do...
}
private currentLMedia: Media;
private localIdStreaming: number;
private localListStreaming: number[] = [];
ngOnInit() {
/*
export interface PlaylistCurrent {
playTrackList: number[];
trackLocalId?: number;
}
*/
let self = this;
this.playerService.playChange.subscribe((newProperty: PlaylistCurrent) => {
self.localIdStreaming = newProperty.trackLocalId;
self.localListStreaming = newProperty.playTrackList;
self.updateMediaStreamed();
});
}
updateMediaStreamed() {
if (isNullOrUndefined(this.localIdStreaming)) {
this.mediaSource = undefined;
return;
}
let self = this;
this.havePrevious = this.localIdStreaming <= 0;
this.haveNext = this.localIdStreaming >= this.localListStreaming.length-1;
this.trackService.get(this.localListStreaming[this.localIdStreaming])
.then((response: Media) => {
//console.log(`get response of video : ${ JSON.stringify(response, null, 2)}`);
self.currentLMedia = response;
self.nameData = response.name;
if (!isNullOrUndefined(response.albumId)) {
this.albumService.get(response.albumId)
.then((response2: NodeData) => {
self.nameData = response2.name + " - " + self.nameData;
this.titleService.setTitle(`${environment.applName} > ${self.nameData}`)
})
.catch(() => {});
}
if (!isNullOrUndefined(response.artists) && isArray(response.artists) && response.artists.length > 0) {
this.artistService.get(response.artists[0])
.then((response2: NodeData) => {
self.nameData = `${self.nameData} (${response2.name})`;
this.titleService.setTitle(`${environment.applName} > ${self.nameData}`)
})
.catch(() => {});
}
if (isNullOrUndefined(self.currentLMedia)) {
console.log("Can not retreive the media ...")
return;
}
if (isNullOrUndefined(self.currentLMedia.dataId)) {
console.log("Can not retreive the media ... null or undefined data ID")
return;
}
self.mediaSource = self.httpService.createRESTCall2({
api: `data/${self.currentLMedia.dataId}/unknowMediaName.webm`, //${self.generatedName}`,
addURLToken: true,
});
}).catch(() => {
console.error("error not ùmanaged in audio player ... 111");
})
}
generateName() {
/*
this.generatedName = '';
if(this.seriesName !== undefined) {
this.generatedName = `${this.generatedName }${this.seriesName }-`;
}
if(this.seasonName !== undefined) {
if(this.seasonName.length < 2) {
this.generatedName = `${this.generatedName }s0${ this.seasonName }-`;
} else {
this.generatedName = `${this.generatedName }s${ this.seasonName }-`;
}
}
if(this.episode !== undefined) {
if(this.episode < 10) {
this.generatedName = `${this.generatedName }e0${ this.episode }-`;
} else {
this.generatedName = `${this.generatedName }e${ this.episode }-`;
}
}
this.generatedName = this.generatedName + this.name;
this.generatedName = this.generatedName.replace(new RegExp('&', 'g'), '_');
this.generatedName = this.generatedName.replace(new RegExp('/', 'g'), '_');
// update the path of the uri request
this.mediaSource = this.httpService.createRESTCall2({
api: `data/${ this.dataId}/${this.generatedName}`,
addURLToken: true,
});
*/
}
myPeriodicCheckFunction() {
console.log('check ... ');
}
changeMetadata() {
console.log('list of the stream:');
/*
let captureStream = this.audioPlayer.audioTracks;
for (let iii=0; iii < captureStream.length; iii++) {
console.log(" - " + captureStream[iii].language);
if (captureStream[iii].language.substring(0,2) === "fr") {
captureStream[iii].enabled = true;
} else {
captureStream[iii].enabled = false;
}
}
*/
}
/*
ngOnInit() {
let self = this;
this.arianeService.updateManual(this.route.snapshot.paramMap);
this.idVideo = this.arianeService.getVideoId();
this.arianeService.mediaChange.subscribe((mediaId) => {
console.log(`Detect mediaId change...${ mediaId}`);
self.idVideo = mediaId;
self.updateDisplay();
});
self.updateDisplay();
}
*/
updateDisplay():void {
let self = this;
/*
self.haveNext = null;
self.havePrevious = null;
this.mediaService.get(this.idVideo)
.then((response) => {
console.log(`get response of media : ${ JSON.stringify(response, null, 2)}`);
self.error = '';
self.name = response.name;
self.description = response.description;
self.episode = response.episode;
self.seriesId = response.seriesId;
self.seasonId = response.seasonId;
self.dataId = response.dataId;
self.time = response.time;
self.generatedName = "????????? TODO: ???????" //response.generatedName;
if(self.dataId !== -1) {
self.mediaSource = self.httpService.createRESTCall2({
api: `data/${ self.dataId}/${self.generatedName}`,
addURLToken: true,
});
} else {
self.mediaSource = '';
}
self.covers = self.dataService.getCoverListUrl(response.covers);
self.generateName();
if(self.seriesId !== undefined && self.seriesId !== null) {
self.seriesService.get(self.seriesId)
.then((response2) => {
self.seriesName = response2.name;
self.generateName();
}).catch((response3) => {
// nothing to do ...
});
}
if(self.seasonId !== undefined && self.seasonId !== null) {
self.seasonService.get(self.seasonId)
.then((response4) => {
self.seasonName = response4.name;
self.generateName();
}).catch((response5) => {
// nothing to do ...
});
self.seasonService.getVideo(self.seasonId)
.then((response6:any) => {
// console.log("saison property: " + JSON.stringify(response, null, 2));
self.haveNext = null;
self.havePrevious = null;
for(let iii = 0; iii < response6.length; iii++) {
if(isNullOrUndefined(response6[iii].episode)) {
continue;
}
if(response6[iii].episode < self.episode) {
if(self.havePrevious === null) {
self.havePrevious = response6[iii];
} else if(self.havePrevious.episode < response6[iii].episode) {
self.havePrevious = response6[iii];
}
} else if(response6[iii].episode > self.episode) {
if(self.haveNext === null) {
self.haveNext = response6[iii];
} else if(self.haveNext.episode > response6[iii].episode) {
self.haveNext = response6[iii];
}
}
self.covers.push(self.dataService.getCoverUrl(response6[iii]));
}
}).catch((response7:any) => {
});
}
self.mediaIsLoading = false;
// console.log("display source " + self.mediaSource);
// console.log("set transformed : " + JSON.stringify(self, null, 2));
}).catch((response8) => {
self.error = 'Can not get the data';
self.name = '';
self.description = '';
self.episode = undefined;
self.seriesId = undefined;
self.seasonId = undefined;
self.dataId = -1;
self.time = undefined;
self.generatedName = '';
self.mediaSource = '';
self.covers = undefined;
self.seriesName = undefined;
self.seasonName = undefined;
self.mediaIsNotFound = true;
self.mediaIsLoading = false;
});
*/
}
onRequirePlay() {
this.playStream = true;
}
onRequireStop() {
this.playStream = false;
}
onAudioEnded() {
if (this.playMode === PlayMode.PLAY_ONE) {
this.playStream = false;
} else if (this.playMode === PlayMode.PLAY_ALL) {
this.onNext();
} else if (this.playMode === PlayMode.PLAY_ONE_LOOP) {
this.mediaPlayer.currentTime = 0;
this.onPlay();
} else {
if (this.localIdStreaming == this.localListStreaming.length-1) {
this.localIdStreaming = 0;
this.updateMediaStreamed();
if (this.localListStreaming.length == 1) {
this.mediaPlayer.currentTime = 0;
this.onPlay();
}
} else {
this.onNext();
}
}
}
changeStateToPlay() {
this.isPlaying = true;
}
changeStateToPause() {
this.isPlaying = false;
}
convertIndisplayTime(time:number) : string {
let tmpp = parseInt(`${ time}`, 10);
let heures = parseInt(`${ tmpp / 3600}`, 10);
tmpp = tmpp - heures * 3600;
let minutes = parseInt(`${ tmpp / 60}`, 10);
let seconds = tmpp - minutes * 60;
let out = '';
if(heures !== 0) {
out = `${out }${heures }:`;
}
if(minutes >= 10) {
out = `${out }${minutes }:`;
} else {
out = `${out }0${ minutes }:`;
}
if(seconds >= 10) {
out = out + seconds;
} else {
out = `${out }0${ seconds}`;
}
return out;
}
changeTimeupdate(currentTime: any) {
// console.log("time change ");
// console.log(" ==> " + this.audioPlayer.currentTime);
this.currentTime = this.mediaPlayer.currentTime;
this.currentTimeDisplay = this.convertIndisplayTime(this.currentTime);
// console.log(" ==> " + this.currentTimeDisplay);
}
changeDurationchange(duration: any) {
console.log('duration change ');
console.log(` ==> ${ this.mediaPlayer.duration}`);
this.duration = this.mediaPlayer.duration;
this.durationDisplay = this.convertIndisplayTime(this.duration);
}
onPlay() {
console.log('play');
if(isNullOrUndefined(this.mediaPlayer)) {
console.log(`error element: ${ this.mediaPlayer}`);
return;
}
this.lockPlayerEnable();
this.mediaPlayer.play();
}
onPause() {
console.log('pause');
if(isNullOrUndefined(this.mediaPlayer)) {
console.log(`error element: ${ this.mediaPlayer}`);
return;
}
this.unlockPlayerEnable();
this.mediaPlayer.pause();
}
onPauseToggle() {
console.log('pause toggle ...');
if(this.isPlaying === true) {
this.onPause();
} else {
this.onPlay();
}
}
onStop() {
console.log('stop');
if(isNullOrUndefined(this.mediaPlayer)) {
console.log(`error element: ${ this.mediaPlayer}`);
return;
}
this.unlockPlayerEnable();
this.mediaPlayer.pause();
this.mediaPlayer.currentTime = 0;
}
onBefore() {
console.log('before');
if (this.localIdStreaming <= 0) {
console.error("have no previous !!!");
return;
}
this.localIdStreaming--;
this.updateMediaStreamed();
this.playerService.previous();
}
onNext() {
console.log('next');
if (this.localIdStreaming >= this.localListStreaming.length-1) {
console.error("have no next !!!");
return;
}
this.localIdStreaming++;
this.updateMediaStreamed();
this.playerService.next();
}
seek(newValue:any) {
console.log(`seek ${ newValue.value}`);
if(isNullOrUndefined(this.mediaPlayer)) {
console.log(`error element: ${ this.mediaPlayer}`);
return;
}
this.mediaPlayer.currentTime = newValue.value;
}
onRewind() {
console.log('rewind');
if(isNullOrUndefined(this.mediaPlayer)) {
console.log(`error element: ${ this.mediaPlayer}`);
return;
}
this.mediaPlayer.currentTime = this.currentTime - 10;
}
onForward() {
console.log('forward');
if(isNullOrUndefined(this.mediaPlayer)) {
console.log(`error element: ${ this.mediaPlayer}`);
return;
}
this.mediaPlayer.currentTime = this.currentTime + 10;
}
onMore() {
console.log('more');
if(isNullOrUndefined(this.mediaPlayer)) {
console.log(`error element: ${ this.mediaPlayer}`);
return;
}
}
// these element is to permit to not stop music when sceen is off !!
wakeLock = null;
unlockPlayerEnable() {
let tmppp = this.wakeLock;
this.wakeLock = null;
if (!isNullOrUndefined(tmppp)) {
tmppp.release();
}
try {
let tmp = navigator as any;
this.wakeLock = tmp.wakeLock.request('screen');
} catch (err) {
// The Wake Lock request has failed - usually system related, such as battery.
console.log(`Can not lock screen element: ${err.name}, ${err.message}`);
}
}
lockPlayerEnable() {
if ('wakeLock' in navigator) {
try {
let tmp = navigator as any;
this.wakeLock = tmp.wakeLock.request('screen');
} catch (err) {
// The Wake Lock request has failed - usually system related, such as battery.
console.log(`Can not lock screen element: ${err.name}, ${err.message}`);
}
}
}
}

View File

@@ -1,41 +0,0 @@
<div class="imgContainer-small">
<div *ngIf="covers">
<!--<data-image id="{{cover}}"></data-image>-->
<img src="{{covers[0]}}"/>
</div>
<div *ngIf="!covers" class="noImage">
</div>
</div>
<div class="season-small">
{{prefixName}} {{numberAlbum}}
</div>
<div class="description-small" *ngIf="countSubType && (!count || count == 0)">
No {{countSubType}}
</div>
<div class="description-small" *ngIf="countSubType && count > 1">
{{count}} {{countSubType}}s
</div>
<div class="description-small" *ngIf="countSubType && count == 1">
{{count}} {{countSubType}}
</div>
<div class="description-small" *ngIf="countSubType2 && (!count2 || count2 == 0)">
No {{countSubType2}}
</div>
<div class="description-small" *ngIf="countSubType2 && count2 > 1">
{{count2}} {{countSubType2}}s
</div>
<div class="description-small" *ngIf="countSubType2 && count2 == 1">
{{count2}} {{countSubType2}}
</div>
<div class="description-small" *ngIf="subValueData">
{{subValues}}: {{subValueData}}
</div>
<div class="description-small" *ngIf="!description">
{{description}}
</div>

View File

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

View File

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

View File

@@ -1,24 +0,0 @@
<div>
<div class="count-base">
<div class="count" *ngIf="counttrack">
{{counttrack}}
</div>
</div>
<div class="imgContainer-small">
<div *ngIf="covers">
<!--<data-image id="{{cover}}"></data-image>-->
<img src="{{covers[0]}}"/>
</div>
<div *ngIf="!covers" class="noImage">
</div>
</div>
<div class="title-small">
{{name}}
</div>
<!--
<div class="description-small" *ngIf="description">
{{description}}
</div>
-->
</div>

View File

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

View File

@@ -1,50 +0,0 @@
/** @file
* @author Edouard DUPIN
* @copyright 2018, Edouard DUPIN, all right reserved
* @license PROPRIETARY (see license file)
*/
import { Component, OnInit, Input } from '@angular/core';
import { NodeData } from 'common/model';
import { isNullOrUndefined } from 'common/utils';
import { ArtistService, DataService } from 'app/service';
@Component({
selector: 'app-element-series',
templateUrl: './element-series.html',
styleUrls: [ './element-series.less' ]
})
export class ElementSeriesComponent implements OnInit {
// input parameters
@Input() element:NodeData;
name:string = 'plouf';
description:string = '';
counttrack:number = null;
covers:string[];
constructor(
private artistService: ArtistService,
private dataService: DataService) {
}
ngOnInit() {
if (isNullOrUndefined(this.element)) {
this.name = '!!ERROR!!';
this.description = undefined;
return;
}
let self = this;
self.name = this.element.name;
self.description = this.element.description;
self.covers = self.dataService.getCoverListThumbnailUrl(this.element.covers);
this.artistService.countTrack(this.element.id)
.then((response) => {
self.counttrack = response;
}).catch((response) => {
self.counttrack = 0;
});
}
}

View File

@@ -1,8 +0,0 @@
<div class="item-list-element">
<div class="season-small">
{{name}}
</div>
<div class="description-small" *ngIf="track">
n° {{track}}
</div>
</div>

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