Compare commits
9 Commits
88b27e5f39
...
401e2ce3c5
Author | SHA1 | Date | |
---|---|---|---|
401e2ce3c5 | |||
693d59ab68 | |||
4eff2e55ef | |||
1e890f9524 | |||
dbb2527cb8 | |||
d65faa8810 | |||
9c9476b052 | |||
3e92c2b74a | |||
a7134c01ed |
@ -1,4 +1,4 @@
|
||||
Karideo
|
||||
Karusic
|
||||
=======
|
||||
|
||||
**K**angaroo **A**nd **R**abbit (m)usic is a simple framework to propose music streaming for personal network
|
||||
|
@ -20,7 +20,7 @@
|
||||
<dependency>
|
||||
<groupId>kangaroo-and-rabbit</groupId>
|
||||
<artifactId>archidata</artifactId>
|
||||
<version>0.23.6</version>
|
||||
<version>0.25.4</version>
|
||||
</dependency>
|
||||
<!-- Loopback of logger JDK logging API to SLF4J -->
|
||||
<dependency>
|
||||
|
@ -13,6 +13,7 @@ import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
|
||||
import org.glassfish.jersey.jackson.JacksonFeature;
|
||||
import org.glassfish.jersey.media.multipart.MultiPartFeature;
|
||||
import org.glassfish.jersey.server.ResourceConfig;
|
||||
import org.glassfish.jersey.server.validation.ValidationFeature;
|
||||
import org.kar.archidata.UpdateJwtPublicKey;
|
||||
import org.kar.archidata.api.DataResource;
|
||||
import org.kar.archidata.api.ProxyResource;
|
||||
@ -151,6 +152,8 @@ public class WebLauncher {
|
||||
|
||||
// add jackson to be discover when we are ins standalone server
|
||||
rc.register(JacksonFeature.class);
|
||||
// enable jersey specific validations (@Valid)
|
||||
rc.register(ValidationFeature.class);
|
||||
// enable this to show low level request
|
||||
// rc.property(LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_SERVER, Level.WARNING.getName());
|
||||
|
||||
|
@ -32,7 +32,6 @@ public class WebLauncherLocal extends WebLauncher {
|
||||
if (true) {
|
||||
// for local test:
|
||||
ConfigBaseVariable.apiAdress = "http://0.0.0.0:19080/karusic/api/";
|
||||
// ConfigBaseVariable.ssoAdress = "https://atria-soft.org/karso/api/";
|
||||
ConfigBaseVariable.dbPort = "3906";
|
||||
ConfigBaseVariable.testMode = "true";
|
||||
}
|
||||
|
@ -6,9 +6,9 @@ import java.util.UUID;
|
||||
|
||||
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
|
||||
import org.glassfish.jersey.media.multipart.FormDataParam;
|
||||
import org.kar.archidata.annotation.AsyncType;
|
||||
import org.kar.archidata.annotation.FormDataOptional;
|
||||
import org.kar.archidata.annotation.TypeScriptProgress;
|
||||
import org.kar.archidata.annotation.apiGenerator.ApiAsyncType;
|
||||
import org.kar.archidata.annotation.apiGenerator.ApiInputOptional;
|
||||
import org.kar.archidata.annotation.apiGenerator.ApiTypeScriptProgress;
|
||||
import org.kar.archidata.dataAccess.DBAccess;
|
||||
import org.kar.archidata.dataAccess.DataAccess;
|
||||
import org.kar.archidata.dataAccess.addOnSQL.AddOnDataJson;
|
||||
@ -74,7 +74,7 @@ public class AlbumResource {
|
||||
@RolesAllowed("ADMIN")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Operation(description = "Update a specific album")
|
||||
public Album patch(@PathParam("id") final Long id, @AsyncType(Album.class) final String jsonRequest) throws Exception {
|
||||
public Album patch(@PathParam("id") final Long id, @ApiAsyncType(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());
|
||||
@ -119,9 +119,9 @@ public class AlbumResource {
|
||||
@RolesAllowed("ADMIN")
|
||||
@Consumes({ MediaType.MULTIPART_FORM_DATA })
|
||||
@Operation(description = "Add a cover on a specific album")
|
||||
@TypeScriptProgress
|
||||
public Album uploadCover(@PathParam("id") final Long id, @FormDataOptional @FormDataParam("uri") final String uri, @FormDataOptional @FormDataParam("file") final InputStream fileInputStream,
|
||||
@FormDataOptional @FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
|
||||
@ApiTypeScriptProgress
|
||||
public Album uploadCover(@PathParam("id") final Long id, @ApiInputOptional @FormDataParam("uri") final String uri, @ApiInputOptional @FormDataParam("file") final InputStream fileInputStream,
|
||||
@ApiInputOptional @FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
|
||||
try (DBAccess db = DBAccess.createInterface()) {
|
||||
if (uri != null) {
|
||||
DataTools.uploadCoverFromUri(db, Album.class, id, uri);
|
||||
|
@ -6,9 +6,8 @@ import java.util.List;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
|
||||
import org.glassfish.jersey.media.multipart.FormDataParam;
|
||||
import org.kar.archidata.annotation.AsyncType;
|
||||
import org.kar.archidata.annotation.FormDataOptional;
|
||||
import org.kar.archidata.annotation.TypeScriptProgress;
|
||||
import org.kar.archidata.annotation.apiGenerator.ApiInputOptional;
|
||||
import org.kar.archidata.annotation.apiGenerator.ApiTypeScriptProgress;
|
||||
import org.kar.archidata.dataAccess.DBAccess;
|
||||
import org.kar.archidata.dataAccess.DataAccess;
|
||||
import org.kar.archidata.dataAccess.addOnSQL.AddOnDataJson;
|
||||
@ -20,11 +19,12 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.PATCH;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
@ -56,12 +56,13 @@ public class ArtistResource {
|
||||
return DataAccess.insert(data, new CheckFunction(CHECKER));
|
||||
}
|
||||
|
||||
@PATCH
|
||||
@PUT
|
||||
@Path("{id}")
|
||||
@RolesAllowed("ADMIN")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public Artist patch(@PathParam("id") final Long id, @AsyncType(Artist.class) final String jsonRequest) throws Exception {
|
||||
DataAccess.updateWithJson(Artist.class, id, jsonRequest, new CheckFunction(CHECKER));
|
||||
public Artist patch(@PathParam("id") final Long id, @Valid final Artist jsonRequest) throws Exception {
|
||||
// new CheckFunction(CHECKER)
|
||||
DataAccess.update(id, jsonRequest);
|
||||
return DataAccess.get(Artist.class, id);
|
||||
}
|
||||
|
||||
@ -76,9 +77,9 @@ public class ArtistResource {
|
||||
@Path("{id}/cover")
|
||||
@RolesAllowed("ADMIN")
|
||||
@Consumes({ MediaType.MULTIPART_FORM_DATA })
|
||||
@TypeScriptProgress
|
||||
public Artist uploadCover(@PathParam("id") final Long id, @FormDataOptional @FormDataParam("uri") final String uri, @FormDataOptional @FormDataParam("file") final InputStream fileInputStream,
|
||||
@FormDataOptional @FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
|
||||
@ApiTypeScriptProgress
|
||||
public Artist uploadCover(@PathParam("id") final Long id, @ApiInputOptional @FormDataParam("uri") final String uri, @ApiInputOptional @FormDataParam("file") final InputStream fileInputStream,
|
||||
@ApiInputOptional @FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
|
||||
try (DBAccess db = DBAccess.createInterface()) {
|
||||
if (uri != null) {
|
||||
DataTools.uploadCoverFromUri(db, Artist.class, id, uri);
|
||||
|
@ -6,9 +6,9 @@ import java.util.List;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
|
||||
import org.glassfish.jersey.media.multipart.FormDataParam;
|
||||
import org.kar.archidata.annotation.AsyncType;
|
||||
import org.kar.archidata.annotation.FormDataOptional;
|
||||
import org.kar.archidata.annotation.TypeScriptProgress;
|
||||
import org.kar.archidata.annotation.apiGenerator.ApiAsyncType;
|
||||
import org.kar.archidata.annotation.apiGenerator.ApiInputOptional;
|
||||
import org.kar.archidata.annotation.apiGenerator.ApiTypeScriptProgress;
|
||||
import org.kar.archidata.dataAccess.DBAccess;
|
||||
import org.kar.archidata.dataAccess.DataAccess;
|
||||
import org.kar.archidata.dataAccess.addOnSQL.AddOnDataJson;
|
||||
@ -60,7 +60,7 @@ public class GenderResource {
|
||||
@Path("{id}")
|
||||
@RolesAllowed("ADMIN")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public Gender patch(@PathParam("id") final Long id, @AsyncType(Gender.class) final String jsonRequest) throws Exception {
|
||||
public Gender patch(@PathParam("id") final Long id, @ApiAsyncType(Gender.class) final String jsonRequest) throws Exception {
|
||||
DataAccess.updateWithJson(Gender.class, id, jsonRequest, new CheckFunction(CHECKER));
|
||||
return DataAccess.get(Gender.class, id);
|
||||
}
|
||||
@ -76,9 +76,9 @@ public class GenderResource {
|
||||
@Path("{id}/cover")
|
||||
@RolesAllowed("ADMIN")
|
||||
@Consumes({ MediaType.MULTIPART_FORM_DATA })
|
||||
@TypeScriptProgress
|
||||
public Gender uploadCover(@PathParam("id") final Long id, @FormDataOptional @FormDataParam("uri") final String uri, @FormDataOptional @FormDataParam("file") final InputStream fileInputStream,
|
||||
@FormDataOptional @FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
|
||||
@ApiTypeScriptProgress
|
||||
public Gender uploadCover(@PathParam("id") final Long id, @ApiInputOptional @FormDataParam("uri") final String uri, @ApiInputOptional @FormDataParam("file") final InputStream fileInputStream,
|
||||
@ApiInputOptional @FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
|
||||
try (DBAccess db = DBAccess.createInterface()) {
|
||||
if (uri != null) {
|
||||
DataTools.uploadCoverFromUri(db, Gender.class, id, uri);
|
||||
|
@ -6,7 +6,7 @@ import java.util.List;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
|
||||
import org.glassfish.jersey.media.multipart.FormDataParam;
|
||||
import org.kar.archidata.annotation.AsyncType;
|
||||
import org.kar.archidata.annotation.apiGenerator.ApiAsyncType;
|
||||
import org.kar.archidata.dataAccess.DBAccess;
|
||||
import org.kar.archidata.dataAccess.DataAccess;
|
||||
import org.kar.archidata.dataAccess.addOnSQL.AddOnDataJson;
|
||||
@ -58,7 +58,7 @@ public class PlaylistResource {
|
||||
@Path("{id}")
|
||||
@RolesAllowed("ADMIN")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public Playlist patch(@PathParam("id") final Long id, @AsyncType(Playlist.class) final String jsonRequest) throws Exception {
|
||||
public Playlist patch(@PathParam("id") final Long id, @ApiAsyncType(Playlist.class) final String jsonRequest) throws Exception {
|
||||
DataAccess.updateWithJson(Playlist.class, id, jsonRequest, new CheckFunction(CHECKER));
|
||||
return DataAccess.get(Playlist.class, id);
|
||||
}
|
||||
@ -95,7 +95,7 @@ public class PlaylistResource {
|
||||
@Path("{id}/cover")
|
||||
@RolesAllowed("ADMIN")
|
||||
@Consumes({ MediaType.MULTIPART_FORM_DATA })
|
||||
@AsyncType(Playlist.class)
|
||||
@ApiAsyncType(Playlist.class)
|
||||
public void uploadCover(@PathParam("id") final Long id, @FormDataParam("file") final InputStream fileInputStream, @FormDataParam("file") final FormDataContentDisposition fileMetaData)
|
||||
throws Exception {
|
||||
try (DBAccess db = DBAccess.createInterface()) {
|
||||
|
@ -9,9 +9,9 @@ import java.util.List;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
|
||||
import org.glassfish.jersey.media.multipart.FormDataParam;
|
||||
import org.kar.archidata.annotation.AsyncType;
|
||||
import org.kar.archidata.annotation.FormDataOptional;
|
||||
import org.kar.archidata.annotation.TypeScriptProgress;
|
||||
import org.kar.archidata.annotation.apiGenerator.ApiAsyncType;
|
||||
import org.kar.archidata.annotation.apiGenerator.ApiInputOptional;
|
||||
import org.kar.archidata.annotation.apiGenerator.ApiTypeScriptProgress;
|
||||
import org.kar.archidata.dataAccess.DBAccess;
|
||||
import org.kar.archidata.dataAccess.DataAccess;
|
||||
import org.kar.archidata.dataAccess.addOnSQL.AddOnDataJson;
|
||||
@ -65,7 +65,7 @@ public class TrackResource {
|
||||
@Path("{id}")
|
||||
@RolesAllowed("ADMIN")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public Track patch(@PathParam("id") final Long id, @AsyncType(Track.class) final String jsonRequest) throws Exception {
|
||||
public Track patch(@PathParam("id") final Long id, @ApiAsyncType(Track.class) final String jsonRequest) throws Exception {
|
||||
DataAccess.updateWithJson(Track.class, id, jsonRequest, new CheckFunction(CHECKER));
|
||||
return DataAccess.get(Track.class, id);
|
||||
}
|
||||
@ -102,7 +102,7 @@ public class TrackResource {
|
||||
@Path("{id}/cover")
|
||||
@RolesAllowed("ADMIN")
|
||||
@Consumes({ MediaType.MULTIPART_FORM_DATA })
|
||||
@TypeScriptProgress
|
||||
@ApiTypeScriptProgress
|
||||
public Track uploadCover(@PathParam("id") final Long id, @FormDataParam("uri") final String uri, @FormDataParam("file") final InputStream fileInputStream,
|
||||
@FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
|
||||
try (DBAccess db = DBAccess.createInterface()) {
|
||||
@ -129,14 +129,14 @@ public class TrackResource {
|
||||
@Path("upload/")
|
||||
@RolesAllowed("ADMIN")
|
||||
@Consumes({ MediaType.MULTIPART_FORM_DATA })
|
||||
@AsyncType(Track.class)
|
||||
@TypeScriptProgress
|
||||
@ApiAsyncType(Track.class)
|
||||
@ApiTypeScriptProgress
|
||||
public Response uploadTrack( //
|
||||
@FormDataParam("title") String title, //
|
||||
@FormDataOptional @AsyncType(Long.class) @FormDataParam("genderId") String genderId, //
|
||||
@FormDataOptional @AsyncType(Long.class) @FormDataParam("artistId") String artistId, //
|
||||
@FormDataOptional @AsyncType(Long.class) @FormDataParam("albumId") String albumId, //
|
||||
@FormDataOptional @AsyncType(Long.class) @FormDataParam("trackId") String trackId, //
|
||||
@ApiInputOptional @ApiAsyncType(Long.class) @FormDataParam("genderId") String genderId, //
|
||||
@ApiInputOptional @ApiAsyncType(Long.class) @FormDataParam("artistId") String artistId, //
|
||||
@ApiInputOptional @ApiAsyncType(Long.class) @FormDataParam("albumId") String albumId, //
|
||||
@ApiInputOptional @ApiAsyncType(Long.class) @FormDataParam("trackId") String trackId, //
|
||||
@FormDataParam("file") final InputStream fileInputStream, //
|
||||
@FormDataParam("file") final FormDataContentDisposition fileMetaData //
|
||||
) {
|
||||
|
@ -2,9 +2,6 @@ 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;
|
||||
|
||||
|
@ -2,11 +2,8 @@ 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;
|
||||
|
@ -10,6 +10,7 @@ import org.kar.archidata.externalRestApi.TsGenerateApi;
|
||||
import org.kar.archidata.migration.MigrationSqlStep;
|
||||
import org.kar.archidata.model.Data;
|
||||
import org.kar.archidata.model.User;
|
||||
import org.kar.archidata.model.token.JwtToken;
|
||||
import org.kar.karusic.api.AlbumResource;
|
||||
import org.kar.karusic.api.ArtistResource;
|
||||
import org.kar.karusic.api.Front;
|
||||
@ -43,6 +44,7 @@ public class Initialization extends MigrationSqlStep {
|
||||
TrackResource.class, DataResource.class, ProxyResource.class);
|
||||
final AnalyzeApi api = new AnalyzeApi();
|
||||
api.addAllApi(listOfResources);
|
||||
api.addModel(JwtToken.class);
|
||||
TsGenerateApi.generateApi(api, "../front/src/back-api/");
|
||||
LOGGER.info("Generate APIs (DONE)");
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import java.util.List;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.kar.archidata.annotation.DataIfNotExists;
|
||||
import org.kar.archidata.annotation.DataJson;
|
||||
import org.kar.archidata.annotation.apiGenerator.ApiGenerationMode;
|
||||
import org.kar.archidata.checker.CheckJPA;
|
||||
import org.kar.archidata.model.Data;
|
||||
import org.kar.archidata.model.GenericDataSoftDelete;
|
||||
@ -22,6 +23,7 @@ import jakarta.persistence.Table;
|
||||
@Table(name = "album")
|
||||
@DataIfNotExists
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@ApiGenerationMode(create = true, update = true)
|
||||
public class Album extends GenericDataSoftDelete {
|
||||
public static class AlbumChecker extends CheckJPA<Album> {
|
||||
public AlbumChecker() {
|
||||
|
@ -6,6 +6,7 @@ import java.util.List;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.kar.archidata.annotation.DataIfNotExists;
|
||||
import org.kar.archidata.annotation.DataJson;
|
||||
import org.kar.archidata.annotation.apiGenerator.ApiGenerationMode;
|
||||
import org.kar.archidata.checker.CheckJPA;
|
||||
import org.kar.archidata.model.Data;
|
||||
import org.kar.archidata.model.GenericDataSoftDelete;
|
||||
@ -22,6 +23,7 @@ import jakarta.persistence.Table;
|
||||
@Table(name = "artist")
|
||||
@DataIfNotExists
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@ApiGenerationMode(create = true, update = true)
|
||||
public class Artist extends GenericDataSoftDelete {
|
||||
public static class ArtistChecker extends CheckJPA<Artist> {
|
||||
public ArtistChecker() {
|
||||
|
@ -17,6 +17,7 @@ import java.util.List;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.kar.archidata.annotation.DataIfNotExists;
|
||||
import org.kar.archidata.annotation.DataJson;
|
||||
import org.kar.archidata.annotation.apiGenerator.ApiGenerationMode;
|
||||
import org.kar.archidata.checker.CheckJPA;
|
||||
import org.kar.archidata.model.Data;
|
||||
import org.kar.archidata.model.GenericDataSoftDelete;
|
||||
@ -33,6 +34,7 @@ import jakarta.persistence.Table;
|
||||
@Table(name = "gender")
|
||||
@DataIfNotExists
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@ApiGenerationMode(create = true, update = true)
|
||||
public class Gender extends GenericDataSoftDelete {
|
||||
public static class GenderChecker extends CheckJPA<Gender> {
|
||||
public GenderChecker() {
|
||||
|
@ -17,6 +17,7 @@ import java.util.List;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.kar.archidata.annotation.DataIfNotExists;
|
||||
import org.kar.archidata.annotation.DataJson;
|
||||
import org.kar.archidata.annotation.apiGenerator.ApiGenerationMode;
|
||||
import org.kar.archidata.checker.CheckJPA;
|
||||
import org.kar.archidata.model.Data;
|
||||
import org.kar.archidata.model.GenericDataSoftDelete;
|
||||
@ -35,6 +36,7 @@ import jakarta.persistence.Table;
|
||||
@Table(name = "playlist")
|
||||
@DataIfNotExists
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@ApiGenerationMode(create = true, update = true)
|
||||
public class Playlist extends GenericDataSoftDelete {
|
||||
public static class PlaylistChecker extends CheckJPA<Playlist> {
|
||||
public PlaylistChecker() {
|
||||
|
@ -17,6 +17,7 @@ import java.util.List;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.kar.archidata.annotation.DataIfNotExists;
|
||||
import org.kar.archidata.annotation.DataJson;
|
||||
import org.kar.archidata.annotation.apiGenerator.ApiGenerationMode;
|
||||
import org.kar.archidata.checker.CheckJPA;
|
||||
import org.kar.archidata.model.Data;
|
||||
import org.kar.archidata.model.GenericDataSoftDelete;
|
||||
@ -33,6 +34,7 @@ import jakarta.persistence.Table;
|
||||
@Table(name = "track")
|
||||
@DataIfNotExists
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@ApiGenerationMode(create = true, update = true)
|
||||
public class Track extends GenericDataSoftDelete {
|
||||
|
||||
public static class TrackChecker extends CheckJPA<Track> {
|
||||
|
@ -29,63 +29,65 @@
|
||||
"*.{ts,tsx,js,jsx,json}": "prettier --write"
|
||||
},
|
||||
"dependencies": {
|
||||
"react-speech-recognition": "4.0.0",
|
||||
"regenerator-runtime": "0.14.1",
|
||||
"@trivago/prettier-plugin-sort-imports": "5.2.2",
|
||||
"@chakra-ui/cli": "3.7.0",
|
||||
"@chakra-ui/react": "3.7.0",
|
||||
"@chakra-ui/cli": "3.13.0",
|
||||
"@chakra-ui/react": "3.13.0",
|
||||
"@emotion/react": "11.14.0",
|
||||
"allotment": "1.20.2",
|
||||
"allotment": "1.20.3",
|
||||
"css-mediaquery": "0.1.2",
|
||||
"dayjs": "1.11.13",
|
||||
"history": "5.3.0",
|
||||
"next-themes": "^0.4.4",
|
||||
"react": "19.0.0-rc.1",
|
||||
"react-dom": "19.0.0-rc.1",
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-error-boundary": "5.0.0",
|
||||
"react-icons": "5.4.0",
|
||||
"react-router-dom": "7.1.5",
|
||||
"react-select": "5.10.0",
|
||||
"react-icons": "5.5.0",
|
||||
"react-router-dom": "7.4.0",
|
||||
"react-select": "5.10.1",
|
||||
"react-use": "17.6.0",
|
||||
"zod": "3.24.1",
|
||||
"zod": "3.24.2",
|
||||
"zustand": "5.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@chakra-ui/styled-system": "^2.12.0",
|
||||
"@playwright/test": "1.50.1",
|
||||
"@storybook/addon-actions": "8.5.4",
|
||||
"@storybook/addon-essentials": "8.5.4",
|
||||
"@storybook/addon-links": "8.5.4",
|
||||
"@storybook/addon-mdx-gfm": "8.5.4",
|
||||
"@storybook/react": "8.5.4",
|
||||
"@storybook/react-vite": "8.5.4",
|
||||
"@storybook/theming": "8.5.4",
|
||||
"@playwright/test": "1.51.1",
|
||||
"@storybook/addon-actions": "8.6.8",
|
||||
"@storybook/addon-essentials": "8.6.8",
|
||||
"@storybook/addon-links": "8.6.8",
|
||||
"@storybook/addon-mdx-gfm": "8.6.8",
|
||||
"@storybook/react": "8.6.8",
|
||||
"@storybook/react-vite": "8.6.8",
|
||||
"@storybook/theming": "8.6.8",
|
||||
"@testing-library/jest-dom": "6.6.3",
|
||||
"@testing-library/react": "16.2.0",
|
||||
"@testing-library/user-event": "14.6.1",
|
||||
"@trivago/prettier-plugin-sort-imports": "5.2.2",
|
||||
"@types/jest": "29.5.14",
|
||||
"@types/node": "22.13.1",
|
||||
"@types/react": "19.0.8",
|
||||
"@types/react-dom": "19.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "8.24.0",
|
||||
"@typescript-eslint/parser": "8.24.0",
|
||||
"@types/node": "22.13.11",
|
||||
"@types/react": "19.0.12",
|
||||
"@types/react-dom": "19.0.4",
|
||||
"@typescript-eslint/eslint-plugin": "8.27.0",
|
||||
"@typescript-eslint/parser": "8.27.0",
|
||||
"@vitejs/plugin-react": "4.3.4",
|
||||
"eslint": "9.20.1",
|
||||
"eslint": "9.23.0",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-react": "7.37.4",
|
||||
"eslint-plugin-react-hooks": "5.1.0",
|
||||
"eslint-plugin-storybook": "0.11.2",
|
||||
"eslint-plugin-react-hooks": "5.2.0",
|
||||
"eslint-plugin-storybook": "0.11.6",
|
||||
"jest": "29.7.0",
|
||||
"jest-environment-jsdom": "29.7.0",
|
||||
"knip": "5.44.0",
|
||||
"lint-staged": "15.4.3",
|
||||
"npm-check-updates": "^17.1.14",
|
||||
"prettier": "3.5.0",
|
||||
"puppeteer": "24.2.0",
|
||||
"knip": "5.46.0",
|
||||
"lint-staged": "15.5.0",
|
||||
"npm-check-updates": "^17.1.16",
|
||||
"prettier": "3.5.3",
|
||||
"puppeteer": "24.4.0",
|
||||
"react-is": "19.0.0",
|
||||
"storybook": "8.5.4",
|
||||
"storybook": "8.6.8",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "5.7.3",
|
||||
"vite": "6.1.0",
|
||||
"vitest": "3.0.5"
|
||||
"typescript": "5.8.2",
|
||||
"vite": "6.2.2",
|
||||
"vitest": "3.0.9"
|
||||
}
|
||||
}
|
||||
|
15
front/pnpm-lock.yaml
generated
15
front/pnpm-lock.yaml
generated
@ -53,9 +53,15 @@ importers:
|
||||
react-select:
|
||||
specifier: 5.10.0
|
||||
version: 5.10.0(@types/react@19.0.8)(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1)
|
||||
react-speech-recognition:
|
||||
specifier: 3.10.0
|
||||
version: 3.10.0(react@19.0.0-rc.1)
|
||||
react-use:
|
||||
specifier: 17.6.0
|
||||
version: 17.6.0(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1)
|
||||
regenerator-runtime:
|
||||
specifier: 0.14.1
|
||||
version: 0.14.1
|
||||
zod:
|
||||
specifier: 3.24.1
|
||||
version: 3.24.1
|
||||
@ -3992,6 +3998,11 @@ packages:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
react-speech-recognition@3.10.0:
|
||||
resolution: {integrity: sha512-EVSr4Ik8l9urwdPiK2r0+ADrLyDDrjB0qBRdUWO+w2MfwEBrj6NuRmy1GD3x7BU/V6/hab0pl8Lupen0zwlJyw==}
|
||||
peerDependencies:
|
||||
react: '>=16.8.0'
|
||||
|
||||
react-transition-group@4.4.5:
|
||||
resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==}
|
||||
peerDependencies:
|
||||
@ -9741,6 +9752,10 @@ snapshots:
|
||||
- '@types/react'
|
||||
- supports-color
|
||||
|
||||
react-speech-recognition@3.10.0(react@19.0.0-rc.1):
|
||||
dependencies:
|
||||
react: 19.0.0-rc.1
|
||||
|
||||
react-transition-group@4.4.5(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.7
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { ErrorBoundary } from '@/errors/ErrorBoundary';
|
||||
|
||||
import { AudioPlayer } from './components';
|
||||
@ -6,6 +8,19 @@ import { AppRoutes } from './scene/AppRoutes';
|
||||
import { ServiceContextProvider } from './service/ServiceContext';
|
||||
|
||||
export const App = () => {
|
||||
// Prevent reload the page when scroll on mobile
|
||||
useEffect(() => {
|
||||
const preventRefresh = (event: { preventDefault: () => void }) => {
|
||||
if (window.scrollY === 0) {
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
document.addEventListener('touchmove', preventRefresh, { passive: false });
|
||||
return () => {
|
||||
document.removeEventListener('touchmove', preventRefresh);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ServiceContextProvider>
|
||||
<EnvDevelopment />
|
||||
|
@ -13,7 +13,8 @@ import {
|
||||
import { z as zod } from "zod"
|
||||
import {
|
||||
Album,
|
||||
AlbumWrite,
|
||||
AlbumCreate,
|
||||
AlbumUpdate,
|
||||
Long,
|
||||
UUID,
|
||||
ZodAlbum,
|
||||
@ -87,7 +88,7 @@ export namespace AlbumResource {
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
data: AlbumWrite,
|
||||
data: Partial<AlbumUpdate>,
|
||||
}): Promise<Album> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
@ -109,7 +110,7 @@ export namespace AlbumResource {
|
||||
data,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
data: AlbumWrite,
|
||||
data: AlbumCreate,
|
||||
}): Promise<Album> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
|
@ -13,7 +13,8 @@ import {
|
||||
import { z as zod } from "zod"
|
||||
import {
|
||||
Artist,
|
||||
ArtistWrite,
|
||||
ArtistCreate,
|
||||
ArtistUpdate,
|
||||
Long,
|
||||
ObjectId,
|
||||
ZodArtist,
|
||||
@ -78,12 +79,12 @@ export namespace ArtistResource {
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
data: ArtistWrite,
|
||||
data: ArtistUpdate,
|
||||
}): Promise<Artist> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
endPoint: "/artist/{id}",
|
||||
requestType: HTTPRequestModel.PATCH,
|
||||
requestType: HTTPRequestModel.PUT,
|
||||
contentType: HTTPMimeType.JSON,
|
||||
accept: HTTPMimeType.JSON,
|
||||
},
|
||||
@ -97,7 +98,7 @@ export namespace ArtistResource {
|
||||
data,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
data: ArtistWrite,
|
||||
data: ArtistCreate,
|
||||
}): Promise<Artist> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
|
@ -22,7 +22,7 @@ export namespace DataResource {
|
||||
restConfig,
|
||||
queries,
|
||||
params,
|
||||
data,
|
||||
headers,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
queries: {
|
||||
@ -32,7 +32,9 @@ export namespace DataResource {
|
||||
name: string,
|
||||
oid: ObjectId,
|
||||
},
|
||||
data: string,
|
||||
headers?: {
|
||||
Range?: string,
|
||||
},
|
||||
}): Promise<object> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
@ -42,7 +44,7 @@ export namespace DataResource {
|
||||
restConfig,
|
||||
params,
|
||||
queries,
|
||||
data,
|
||||
headers,
|
||||
});
|
||||
};
|
||||
/**
|
||||
@ -52,7 +54,7 @@ export namespace DataResource {
|
||||
restConfig,
|
||||
queries,
|
||||
params,
|
||||
data,
|
||||
headers,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
queries: {
|
||||
@ -61,7 +63,9 @@ export namespace DataResource {
|
||||
params: {
|
||||
oid: ObjectId,
|
||||
},
|
||||
data: string,
|
||||
headers?: {
|
||||
Range: string,
|
||||
},
|
||||
}): Promise<object> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
@ -71,7 +75,7 @@ export namespace DataResource {
|
||||
restConfig,
|
||||
params,
|
||||
queries,
|
||||
data,
|
||||
headers,
|
||||
});
|
||||
};
|
||||
/**
|
||||
@ -81,7 +85,7 @@ export namespace DataResource {
|
||||
restConfig,
|
||||
queries,
|
||||
params,
|
||||
data,
|
||||
headers,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
queries: {
|
||||
@ -90,7 +94,9 @@ export namespace DataResource {
|
||||
params: {
|
||||
oid: ObjectId,
|
||||
},
|
||||
data: string,
|
||||
headers?: {
|
||||
Range: string,
|
||||
},
|
||||
}): Promise<object> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
@ -100,7 +106,7 @@ export namespace DataResource {
|
||||
restConfig,
|
||||
params,
|
||||
queries,
|
||||
data,
|
||||
headers,
|
||||
});
|
||||
};
|
||||
/**
|
||||
|
@ -13,7 +13,8 @@ import {
|
||||
import { z as zod } from "zod"
|
||||
import {
|
||||
Gender,
|
||||
GenderWrite,
|
||||
GenderCreate,
|
||||
GenderUpdate,
|
||||
Long,
|
||||
ObjectId,
|
||||
ZodGender,
|
||||
@ -78,7 +79,7 @@ export namespace GenderResource {
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
data: GenderWrite,
|
||||
data: Partial<GenderUpdate>,
|
||||
}): Promise<Gender> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
@ -97,7 +98,7 @@ export namespace GenderResource {
|
||||
data,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
data: GenderWrite,
|
||||
data: GenderCreate,
|
||||
}): Promise<Gender> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
|
@ -14,7 +14,8 @@ import {
|
||||
Long,
|
||||
ObjectId,
|
||||
Playlist,
|
||||
PlaylistWrite,
|
||||
PlaylistCreate,
|
||||
PlaylistUpdate,
|
||||
ZodPlaylist,
|
||||
isPlaylist,
|
||||
} from "../model";
|
||||
@ -98,7 +99,7 @@ export namespace PlaylistResource {
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
data: PlaylistWrite,
|
||||
data: Partial<PlaylistUpdate>,
|
||||
}): Promise<Playlist> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
@ -117,7 +118,7 @@ export namespace PlaylistResource {
|
||||
data,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
data: PlaylistWrite,
|
||||
data: PlaylistCreate,
|
||||
}): Promise<Playlist> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
|
@ -15,7 +15,8 @@ import {
|
||||
Long,
|
||||
ObjectId,
|
||||
Track,
|
||||
TrackWrite,
|
||||
TrackCreate,
|
||||
TrackUpdate,
|
||||
ZodTrack,
|
||||
isTrack,
|
||||
} from "../model";
|
||||
@ -99,7 +100,7 @@ export namespace TrackResource {
|
||||
params: {
|
||||
id: Long,
|
||||
},
|
||||
data: TrackWrite,
|
||||
data: Partial<TrackUpdate>,
|
||||
}): Promise<Track> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
@ -118,7 +119,7 @@ export namespace TrackResource {
|
||||
data,
|
||||
}: {
|
||||
restConfig: RESTConfig,
|
||||
data: TrackWrite,
|
||||
data: TrackCreate,
|
||||
}): Promise<Track> {
|
||||
return RESTRequestJson({
|
||||
restModel: {
|
||||
|
@ -5,7 +5,7 @@ import { z as zod } from "zod";
|
||||
|
||||
import {ZodObjectId} from "./object-id";
|
||||
import {ZodLocalDate} from "./local-date";
|
||||
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteWrite } from "./generic-data-soft-delete";
|
||||
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteUpdate , ZodGenericDataSoftDeleteCreate } from "./generic-data-soft-delete";
|
||||
|
||||
export const ZodAlbum = ZodGenericDataSoftDelete.extend({
|
||||
name: zod.string().optional(),
|
||||
@ -29,7 +29,7 @@ export function isAlbum(data: any): data is Album {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodAlbumWrite = ZodGenericDataSoftDeleteWrite.extend({
|
||||
export const ZodAlbumUpdate = ZodGenericDataSoftDeleteUpdate.extend({
|
||||
name: zod.string().nullable().optional(),
|
||||
description: zod.string().nullable().optional(),
|
||||
/**
|
||||
@ -40,14 +40,36 @@ export const ZodAlbumWrite = ZodGenericDataSoftDeleteWrite.extend({
|
||||
|
||||
});
|
||||
|
||||
export type AlbumWrite = zod.infer<typeof ZodAlbumWrite>;
|
||||
export type AlbumUpdate = zod.infer<typeof ZodAlbumUpdate>;
|
||||
|
||||
export function isAlbumWrite(data: any): data is AlbumWrite {
|
||||
export function isAlbumUpdate(data: any): data is AlbumUpdate {
|
||||
try {
|
||||
ZodAlbumWrite.parse(data);
|
||||
ZodAlbumUpdate.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodAlbumWrite' error=${e}`);
|
||||
console.log(`Fail to parse data type='ZodAlbumUpdate' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodAlbumCreate = ZodGenericDataSoftDeleteCreate.extend({
|
||||
name: zod.string().nullable().optional(),
|
||||
description: zod.string().nullable().optional(),
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodObjectId).nullable().optional(),
|
||||
publication: ZodLocalDate.nullable().optional(),
|
||||
|
||||
});
|
||||
|
||||
export type AlbumCreate = zod.infer<typeof ZodAlbumCreate>;
|
||||
|
||||
export function isAlbumCreate(data: any): data is AlbumCreate {
|
||||
try {
|
||||
ZodAlbumCreate.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodAlbumCreate' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import { z as zod } from "zod";
|
||||
|
||||
import {ZodObjectId} from "./object-id";
|
||||
import {ZodLocalDate} from "./local-date";
|
||||
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteWrite } from "./generic-data-soft-delete";
|
||||
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteUpdate , ZodGenericDataSoftDeleteCreate } from "./generic-data-soft-delete";
|
||||
|
||||
export const ZodArtist = ZodGenericDataSoftDelete.extend({
|
||||
name: zod.string().optional(),
|
||||
@ -32,7 +32,7 @@ export function isArtist(data: any): data is Artist {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodArtistWrite = ZodGenericDataSoftDeleteWrite.extend({
|
||||
export const ZodArtistUpdate = ZodGenericDataSoftDeleteUpdate.extend({
|
||||
name: zod.string().nullable().optional(),
|
||||
description: zod.string().nullable().optional(),
|
||||
/**
|
||||
@ -46,14 +46,39 @@ export const ZodArtistWrite = ZodGenericDataSoftDeleteWrite.extend({
|
||||
|
||||
});
|
||||
|
||||
export type ArtistWrite = zod.infer<typeof ZodArtistWrite>;
|
||||
export type ArtistUpdate = zod.infer<typeof ZodArtistUpdate>;
|
||||
|
||||
export function isArtistWrite(data: any): data is ArtistWrite {
|
||||
export function isArtistUpdate(data: any): data is ArtistUpdate {
|
||||
try {
|
||||
ZodArtistWrite.parse(data);
|
||||
ZodArtistUpdate.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodArtistWrite' error=${e}`);
|
||||
console.log(`Fail to parse data type='ZodArtistUpdate' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodArtistCreate = ZodGenericDataSoftDeleteCreate.extend({
|
||||
name: zod.string().nullable().optional(),
|
||||
description: zod.string().nullable().optional(),
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodObjectId).nullable().optional(),
|
||||
firstName: zod.string().nullable().optional(),
|
||||
surname: zod.string().nullable().optional(),
|
||||
birth: ZodLocalDate.nullable().optional(),
|
||||
death: ZodLocalDate.nullable().optional(),
|
||||
|
||||
});
|
||||
|
||||
export type ArtistCreate = zod.infer<typeof ZodArtistCreate>;
|
||||
|
||||
export function isArtistCreate(data: any): data is ArtistCreate {
|
||||
try {
|
||||
ZodArtistCreate.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodArtistCreate' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodObjectId} from "./object-id";
|
||||
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteWrite } from "./generic-data-soft-delete";
|
||||
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteUpdate , ZodGenericDataSoftDeleteCreate } from "./generic-data-soft-delete";
|
||||
|
||||
export const ZodGender = ZodGenericDataSoftDelete.extend({
|
||||
name: zod.string().optional(),
|
||||
@ -27,7 +27,7 @@ export function isGender(data: any): data is Gender {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodGenderWrite = ZodGenericDataSoftDeleteWrite.extend({
|
||||
export const ZodGenderUpdate = ZodGenericDataSoftDeleteUpdate.extend({
|
||||
name: zod.string().nullable().optional(),
|
||||
description: zod.string().nullable().optional(),
|
||||
/**
|
||||
@ -37,14 +37,35 @@ export const ZodGenderWrite = ZodGenericDataSoftDeleteWrite.extend({
|
||||
|
||||
});
|
||||
|
||||
export type GenderWrite = zod.infer<typeof ZodGenderWrite>;
|
||||
export type GenderUpdate = zod.infer<typeof ZodGenderUpdate>;
|
||||
|
||||
export function isGenderWrite(data: any): data is GenderWrite {
|
||||
export function isGenderUpdate(data: any): data is GenderUpdate {
|
||||
try {
|
||||
ZodGenderWrite.parse(data);
|
||||
ZodGenderUpdate.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodGenderWrite' error=${e}`);
|
||||
console.log(`Fail to parse data type='ZodGenderUpdate' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodGenderCreate = ZodGenericDataSoftDeleteCreate.extend({
|
||||
name: zod.string().nullable().optional(),
|
||||
description: zod.string().nullable().optional(),
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodObjectId).nullable().optional(),
|
||||
|
||||
});
|
||||
|
||||
export type GenderCreate = zod.infer<typeof ZodGenderCreate>;
|
||||
|
||||
export function isGenderCreate(data: any): data is GenderCreate {
|
||||
try {
|
||||
ZodGenderCreate.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodGenderCreate' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodGenericData, ZodGenericDataWrite } from "./generic-data";
|
||||
import {ZodGenericData, ZodGenericDataUpdate , ZodGenericDataCreate } from "./generic-data";
|
||||
|
||||
export const ZodGenericDataSoftDelete = ZodGenericData.extend({
|
||||
/**
|
||||
@ -24,16 +24,29 @@ export function isGenericDataSoftDelete(data: any): data is GenericDataSoftDelet
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodGenericDataSoftDeleteWrite = ZodGenericDataWrite;
|
||||
export const ZodGenericDataSoftDeleteUpdate = ZodGenericDataUpdate;
|
||||
|
||||
export type GenericDataSoftDeleteWrite = zod.infer<typeof ZodGenericDataSoftDeleteWrite>;
|
||||
export type GenericDataSoftDeleteUpdate = zod.infer<typeof ZodGenericDataSoftDeleteUpdate>;
|
||||
|
||||
export function isGenericDataSoftDeleteWrite(data: any): data is GenericDataSoftDeleteWrite {
|
||||
export function isGenericDataSoftDeleteUpdate(data: any): data is GenericDataSoftDeleteUpdate {
|
||||
try {
|
||||
ZodGenericDataSoftDeleteWrite.parse(data);
|
||||
ZodGenericDataSoftDeleteUpdate.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodGenericDataSoftDeleteWrite' error=${e}`);
|
||||
console.log(`Fail to parse data type='ZodGenericDataSoftDeleteUpdate' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodGenericDataSoftDeleteCreate = ZodGenericDataCreate;
|
||||
|
||||
export type GenericDataSoftDeleteCreate = zod.infer<typeof ZodGenericDataSoftDeleteCreate>;
|
||||
|
||||
export function isGenericDataSoftDeleteCreate(data: any): data is GenericDataSoftDeleteCreate {
|
||||
try {
|
||||
ZodGenericDataSoftDeleteCreate.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodGenericDataSoftDeleteCreate' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodLong} from "./long";
|
||||
import {ZodGenericTiming, ZodGenericTimingWrite } from "./generic-timing";
|
||||
import {ZodGenericTiming, ZodGenericTimingUpdate , ZodGenericTimingCreate } from "./generic-timing";
|
||||
|
||||
export const ZodGenericData = ZodGenericTiming.extend({
|
||||
/**
|
||||
@ -25,16 +25,29 @@ export function isGenericData(data: any): data is GenericData {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodGenericDataWrite = ZodGenericTimingWrite;
|
||||
export const ZodGenericDataUpdate = ZodGenericTimingUpdate;
|
||||
|
||||
export type GenericDataWrite = zod.infer<typeof ZodGenericDataWrite>;
|
||||
export type GenericDataUpdate = zod.infer<typeof ZodGenericDataUpdate>;
|
||||
|
||||
export function isGenericDataWrite(data: any): data is GenericDataWrite {
|
||||
export function isGenericDataUpdate(data: any): data is GenericDataUpdate {
|
||||
try {
|
||||
ZodGenericDataWrite.parse(data);
|
||||
ZodGenericDataUpdate.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodGenericDataWrite' error=${e}`);
|
||||
console.log(`Fail to parse data type='ZodGenericDataUpdate' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodGenericDataCreate = ZodGenericTimingCreate;
|
||||
|
||||
export type GenericDataCreate = zod.infer<typeof ZodGenericDataCreate>;
|
||||
|
||||
export function isGenericDataCreate(data: any): data is GenericDataCreate {
|
||||
try {
|
||||
ZodGenericDataCreate.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodGenericDataCreate' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -28,18 +28,33 @@ export function isGenericTiming(data: any): data is GenericTiming {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodGenericTimingWrite = zod.object({
|
||||
export const ZodGenericTimingUpdate = zod.object({
|
||||
|
||||
});
|
||||
|
||||
export type GenericTimingWrite = zod.infer<typeof ZodGenericTimingWrite>;
|
||||
export type GenericTimingUpdate = zod.infer<typeof ZodGenericTimingUpdate>;
|
||||
|
||||
export function isGenericTimingWrite(data: any): data is GenericTimingWrite {
|
||||
export function isGenericTimingUpdate(data: any): data is GenericTimingUpdate {
|
||||
try {
|
||||
ZodGenericTimingWrite.parse(data);
|
||||
ZodGenericTimingUpdate.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodGenericTimingWrite' error=${e}`);
|
||||
console.log(`Fail to parse data type='ZodGenericTimingUpdate' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodGenericTimingCreate = zod.object({
|
||||
|
||||
});
|
||||
|
||||
export type GenericTimingCreate = zod.infer<typeof ZodGenericTimingCreate>;
|
||||
|
||||
export function isGenericTimingCreate(data: any): data is GenericTimingCreate {
|
||||
try {
|
||||
ZodGenericTimingCreate.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodGenericTimingCreate' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -19,18 +19,3 @@ export function isHealthResult(data: any): data is HealthResult {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodHealthResultWrite = zod.object({
|
||||
|
||||
});
|
||||
|
||||
export type HealthResultWrite = zod.infer<typeof ZodHealthResultWrite>;
|
||||
|
||||
export function isHealthResultWrite(data: any): data is HealthResultWrite {
|
||||
try {
|
||||
ZodHealthResultWrite.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodHealthResultWrite' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -10,12 +10,16 @@ export * from "./generic-timing"
|
||||
export * from "./health-result"
|
||||
export * from "./integer"
|
||||
export * from "./iso-date"
|
||||
export * from "./jwt-header"
|
||||
export * from "./jwt-payload"
|
||||
export * from "./jwt-token"
|
||||
export * from "./local-date"
|
||||
export * from "./long"
|
||||
export * from "./object-id"
|
||||
export * from "./part-right"
|
||||
export * from "./playlist"
|
||||
export * from "./rest-error-response"
|
||||
export * from "./rest-input-error"
|
||||
export * from "./timestamp"
|
||||
export * from "./track"
|
||||
export * from "./user"
|
||||
|
23
front/src/back-api/model/jwt-header.ts
Normal file
23
front/src/back-api/model/jwt-header.ts
Normal file
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
|
||||
export const ZodJwtHeader = zod.object({
|
||||
typ: zod.string().max(128),
|
||||
alg: zod.string().max(128),
|
||||
|
||||
});
|
||||
|
||||
export type JwtHeader = zod.infer<typeof ZodJwtHeader>;
|
||||
|
||||
export function isJwtHeader(data: any): data is JwtHeader {
|
||||
try {
|
||||
ZodJwtHeader.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodJwtHeader' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
29
front/src/back-api/model/jwt-payload.ts
Normal file
29
front/src/back-api/model/jwt-payload.ts
Normal file
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodLong} from "./long";
|
||||
|
||||
export const ZodJwtPayload = zod.object({
|
||||
sub: zod.string(),
|
||||
application: zod.string(),
|
||||
iss: zod.string(),
|
||||
right: zod.record(zod.string(), zod.record(zod.string(), ZodLong)),
|
||||
login: zod.string(),
|
||||
exp: ZodLong,
|
||||
iat: ZodLong,
|
||||
|
||||
});
|
||||
|
||||
export type JwtPayload = zod.infer<typeof ZodJwtPayload>;
|
||||
|
||||
export function isJwtPayload(data: any): data is JwtPayload {
|
||||
try {
|
||||
ZodJwtPayload.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodJwtPayload' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
26
front/src/back-api/model/jwt-token.ts
Normal file
26
front/src/back-api/model/jwt-token.ts
Normal file
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodJwtHeader} from "./jwt-header";
|
||||
import {ZodJwtPayload} from "./jwt-payload";
|
||||
|
||||
export const ZodJwtToken = zod.object({
|
||||
header: ZodJwtHeader,
|
||||
payload: ZodJwtPayload,
|
||||
signature: zod.string(),
|
||||
|
||||
});
|
||||
|
||||
export type JwtToken = zod.infer<typeof ZodJwtToken>;
|
||||
|
||||
export function isJwtToken(data: any): data is JwtToken {
|
||||
try {
|
||||
ZodJwtToken.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodJwtToken' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodObjectId} from "./object-id";
|
||||
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteWrite } from "./generic-data-soft-delete";
|
||||
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteUpdate , ZodGenericDataSoftDeleteCreate } from "./generic-data-soft-delete";
|
||||
|
||||
export const ZodPlaylist = ZodGenericDataSoftDelete.extend({
|
||||
name: zod.string().optional(),
|
||||
@ -28,25 +28,47 @@ export function isPlaylist(data: any): data is Playlist {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodPlaylistWrite = ZodGenericDataSoftDeleteWrite.extend({
|
||||
export const ZodPlaylistUpdate = ZodGenericDataSoftDeleteUpdate.extend({
|
||||
name: zod.string().nullable().optional(),
|
||||
description: zod.string().nullable().optional(),
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodObjectId).nullable().optional(),
|
||||
tracks: zod.array(ZodObjectId).optional(),
|
||||
tracks: zod.array(ZodObjectId),
|
||||
|
||||
});
|
||||
|
||||
export type PlaylistWrite = zod.infer<typeof ZodPlaylistWrite>;
|
||||
export type PlaylistUpdate = zod.infer<typeof ZodPlaylistUpdate>;
|
||||
|
||||
export function isPlaylistWrite(data: any): data is PlaylistWrite {
|
||||
export function isPlaylistUpdate(data: any): data is PlaylistUpdate {
|
||||
try {
|
||||
ZodPlaylistWrite.parse(data);
|
||||
ZodPlaylistUpdate.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodPlaylistWrite' error=${e}`);
|
||||
console.log(`Fail to parse data type='ZodPlaylistUpdate' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodPlaylistCreate = ZodGenericDataSoftDeleteCreate.extend({
|
||||
name: zod.string().nullable().optional(),
|
||||
description: zod.string().nullable().optional(),
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodObjectId).nullable().optional(),
|
||||
tracks: zod.array(ZodObjectId),
|
||||
|
||||
});
|
||||
|
||||
export type PlaylistCreate = zod.infer<typeof ZodPlaylistCreate>;
|
||||
|
||||
export function isPlaylistCreate(data: any): data is PlaylistCreate {
|
||||
try {
|
||||
ZodPlaylistCreate.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodPlaylistCreate' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import { z as zod } from "zod";
|
||||
|
||||
import {ZodObjectId} from "./object-id";
|
||||
import {ZodInteger} from "./integer";
|
||||
import {ZodRestInputError} from "./rest-input-error";
|
||||
|
||||
export const ZodRestErrorResponse = zod.object({
|
||||
oid: ZodObjectId.optional(),
|
||||
@ -13,6 +14,7 @@ export const ZodRestErrorResponse = zod.object({
|
||||
time: zod.string(),
|
||||
status: ZodInteger,
|
||||
statusMessage: zod.string(),
|
||||
inputError: zod.array(ZodRestInputError).optional(),
|
||||
|
||||
});
|
||||
|
||||
|
24
front/src/back-api/model/rest-input-error.ts
Normal file
24
front/src/back-api/model/rest-input-error.ts
Normal file
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
|
||||
export const ZodRestInputError = zod.object({
|
||||
argument: zod.string().optional(),
|
||||
path: zod.string().optional(),
|
||||
message: zod.string(),
|
||||
|
||||
});
|
||||
|
||||
export type RestInputError = zod.infer<typeof ZodRestInputError>;
|
||||
|
||||
export function isRestInputError(data: any): data is RestInputError {
|
||||
try {
|
||||
ZodRestInputError.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodRestInputError' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ import { z as zod } from "zod";
|
||||
|
||||
import {ZodObjectId} from "./object-id";
|
||||
import {ZodLong} from "./long";
|
||||
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteWrite } from "./generic-data-soft-delete";
|
||||
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteUpdate , ZodGenericDataSoftDeleteCreate } from "./generic-data-soft-delete";
|
||||
|
||||
export const ZodTrack = ZodGenericDataSoftDelete.extend({
|
||||
name: zod.string().optional(),
|
||||
@ -33,7 +33,7 @@ export function isTrack(data: any): data is Track {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodTrackWrite = ZodGenericDataSoftDeleteWrite.extend({
|
||||
export const ZodTrackUpdate = ZodGenericDataSoftDeleteUpdate.extend({
|
||||
name: zod.string().nullable().optional(),
|
||||
description: zod.string().nullable().optional(),
|
||||
/**
|
||||
@ -44,18 +44,44 @@ export const ZodTrackWrite = ZodGenericDataSoftDeleteWrite.extend({
|
||||
albumId: ZodLong.nullable().optional(),
|
||||
track: ZodLong.nullable().optional(),
|
||||
dataId: ZodObjectId.nullable().optional(),
|
||||
artists: zod.array(ZodLong).optional(),
|
||||
artists: zod.array(ZodLong),
|
||||
|
||||
});
|
||||
|
||||
export type TrackWrite = zod.infer<typeof ZodTrackWrite>;
|
||||
export type TrackUpdate = zod.infer<typeof ZodTrackUpdate>;
|
||||
|
||||
export function isTrackWrite(data: any): data is TrackWrite {
|
||||
export function isTrackUpdate(data: any): data is TrackUpdate {
|
||||
try {
|
||||
ZodTrackWrite.parse(data);
|
||||
ZodTrackUpdate.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodTrackWrite' error=${e}`);
|
||||
console.log(`Fail to parse data type='ZodTrackUpdate' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodTrackCreate = ZodGenericDataSoftDeleteCreate.extend({
|
||||
name: zod.string().nullable().optional(),
|
||||
description: zod.string().nullable().optional(),
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodObjectId).nullable().optional(),
|
||||
genderId: ZodLong.nullable().optional(),
|
||||
albumId: ZodLong.nullable().optional(),
|
||||
track: ZodLong.nullable().optional(),
|
||||
dataId: ZodObjectId.nullable().optional(),
|
||||
artists: zod.array(ZodLong),
|
||||
|
||||
});
|
||||
|
||||
export type TrackCreate = zod.infer<typeof ZodTrackCreate>;
|
||||
|
||||
export function isTrackCreate(data: any): data is TrackCreate {
|
||||
try {
|
||||
ZodTrackCreate.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodTrackCreate' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodUser, ZodUserWrite } from "./user";
|
||||
import {ZodUser, ZodUserUpdate , ZodUserCreate } from "./user";
|
||||
|
||||
export const ZodUserKarusic = ZodUser;
|
||||
|
||||
@ -18,16 +18,3 @@ export function isUserKarusic(data: any): data is UserKarusic {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodUserKarusicWrite = ZodUserWrite;
|
||||
|
||||
export type UserKarusicWrite = zod.infer<typeof ZodUserKarusicWrite>;
|
||||
|
||||
export function isUserKarusicWrite(data: any): data is UserKarusicWrite {
|
||||
try {
|
||||
ZodUserKarusicWrite.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodUserKarusicWrite' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import { z as zod } from "zod";
|
||||
|
||||
import {ZodTimestamp} from "./timestamp";
|
||||
import {ZodUUID} from "./uuid";
|
||||
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteWrite } from "./generic-data-soft-delete";
|
||||
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteUpdate , ZodGenericDataSoftDeleteCreate } from "./generic-data-soft-delete";
|
||||
|
||||
export const ZodUser = ZodGenericDataSoftDelete.extend({
|
||||
login: zod.string().min(3).max(128),
|
||||
@ -30,8 +30,8 @@ export function isUser(data: any): data is User {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodUserWrite = ZodGenericDataSoftDeleteWrite.extend({
|
||||
login: zod.string().min(3).max(128).optional(),
|
||||
export const ZodUserUpdate = ZodGenericDataSoftDeleteUpdate.extend({
|
||||
login: zod.string().min(3).max(128),
|
||||
lastConnection: ZodTimestamp.nullable().optional(),
|
||||
blocked: zod.boolean().nullable().optional(),
|
||||
blockedReason: zod.string().max(512).nullable().optional(),
|
||||
@ -42,14 +42,37 @@ export const ZodUserWrite = ZodGenericDataSoftDeleteWrite.extend({
|
||||
|
||||
});
|
||||
|
||||
export type UserWrite = zod.infer<typeof ZodUserWrite>;
|
||||
export type UserUpdate = zod.infer<typeof ZodUserUpdate>;
|
||||
|
||||
export function isUserWrite(data: any): data is UserWrite {
|
||||
export function isUserUpdate(data: any): data is UserUpdate {
|
||||
try {
|
||||
ZodUserWrite.parse(data);
|
||||
ZodUserUpdate.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodUserWrite' error=${e}`);
|
||||
console.log(`Fail to parse data type='ZodUserUpdate' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodUserCreate = ZodGenericDataSoftDeleteCreate.extend({
|
||||
login: zod.string().min(3).max(128),
|
||||
lastConnection: ZodTimestamp.nullable().optional(),
|
||||
blocked: zod.boolean().nullable().optional(),
|
||||
blockedReason: zod.string().max(512).nullable().optional(),
|
||||
/**
|
||||
* List of Id of the specific covers
|
||||
*/
|
||||
covers: zod.array(ZodUUID).nullable().optional(),
|
||||
|
||||
});
|
||||
|
||||
export type UserCreate = zod.infer<typeof ZodUserCreate>;
|
||||
|
||||
export function isUserCreate(data: any): data is UserCreate {
|
||||
try {
|
||||
ZodUserCreate.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodUserCreate' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -3,30 +3,29 @@
|
||||
* @copyright 2024, Edouard DUPIN, all right reserved
|
||||
* @license MPL-2
|
||||
*/
|
||||
|
||||
import { RestErrorResponse, isRestErrorResponse } from "./model";
|
||||
import { RestErrorResponse, isRestErrorResponse } from './model';
|
||||
|
||||
export enum HTTPRequestModel {
|
||||
ARCHIVE = "ARCHIVE",
|
||||
DELETE = "DELETE",
|
||||
HEAD = "HEAD",
|
||||
GET = "GET",
|
||||
OPTION = "OPTION",
|
||||
PATCH = "PATCH",
|
||||
POST = "POST",
|
||||
PUT = "PUT",
|
||||
RESTORE = "RESTORE",
|
||||
ARCHIVE = 'ARCHIVE',
|
||||
DELETE = 'DELETE',
|
||||
HEAD = 'HEAD',
|
||||
GET = 'GET',
|
||||
OPTION = 'OPTION',
|
||||
PATCH = 'PATCH',
|
||||
POST = 'POST',
|
||||
PUT = 'PUT',
|
||||
RESTORE = 'RESTORE',
|
||||
}
|
||||
export enum HTTPMimeType {
|
||||
ALL = "*/*",
|
||||
CSV = "text/csv",
|
||||
IMAGE = "image/*",
|
||||
IMAGE_JPEG = "image/jpeg",
|
||||
IMAGE_PNG = "image/png",
|
||||
JSON = "application/json",
|
||||
MULTIPART = "multipart/form-data",
|
||||
OCTET_STREAM = "application/octet-stream",
|
||||
TEXT_PLAIN = "text/plain",
|
||||
ALL = '*/*',
|
||||
CSV = 'text/csv',
|
||||
IMAGE = 'image/*',
|
||||
IMAGE_JPEG = 'image/jpeg',
|
||||
IMAGE_PNG = 'image/png',
|
||||
JSON = 'application/json',
|
||||
MULTIPART = 'multipart/form-data',
|
||||
OCTET_STREAM = 'application/octet-stream',
|
||||
TEXT_PLAIN = 'text/plain',
|
||||
}
|
||||
|
||||
export interface RESTConfig {
|
||||
@ -54,6 +53,14 @@ export interface ModelResponseHttp {
|
||||
data: any;
|
||||
}
|
||||
|
||||
export type ErrorRestApiCallback = (response: Response) => void;
|
||||
|
||||
let errorApiGlobalCallback: ErrorRestApiCallback | undefined = undefined;
|
||||
|
||||
export const setErrorApiGlobalCallback = (callback: ErrorRestApiCallback) => {
|
||||
errorApiGlobalCallback = callback;
|
||||
};
|
||||
|
||||
function isNullOrUndefined(data: any): data is undefined | null {
|
||||
return data === undefined || data === null;
|
||||
}
|
||||
@ -78,6 +85,7 @@ export interface RESTRequestType {
|
||||
data?: any;
|
||||
params?: object;
|
||||
queries?: object;
|
||||
headers?: any;
|
||||
callbacks?: RESTCallbacks;
|
||||
}
|
||||
|
||||
@ -87,15 +95,15 @@ function replaceAll(input, searchValue, replaceValue) {
|
||||
|
||||
function removeTrailingSlashes(input: string): string {
|
||||
if (isNullOrUndefined(input)) {
|
||||
return "undefined";
|
||||
return 'undefined';
|
||||
}
|
||||
return input.replace(/\/+$/, "");
|
||||
return input.replace(/\/+$/, '');
|
||||
}
|
||||
function removeLeadingSlashes(input: string): string {
|
||||
if (isNullOrUndefined(input)) {
|
||||
return "";
|
||||
return '';
|
||||
}
|
||||
return input.replace(/^\/+/, "");
|
||||
return input.replace(/^\/+/, '');
|
||||
}
|
||||
|
||||
export function RESTUrl({
|
||||
@ -133,9 +141,9 @@ export function RESTUrl({
|
||||
}
|
||||
}
|
||||
if (restConfig.token !== undefined && restModel.tokenInUrl === true) {
|
||||
searchParams.append("Authorization", `Bearer ${restConfig.token}`);
|
||||
searchParams.append('Authorization', `Bearer ${restConfig.token}`);
|
||||
}
|
||||
return generateUrl + "?" + searchParams.toString();
|
||||
return generateUrl + '?' + searchParams.toString();
|
||||
}
|
||||
|
||||
export function fetchProgress(
|
||||
@ -159,7 +167,7 @@ export function fetchProgress(
|
||||
return new Promise((resolve, reject) => {
|
||||
// Stream the upload progress
|
||||
if (progressUpload) {
|
||||
xhr.io?.upload.addEventListener("progress", (dataEvent) => {
|
||||
xhr.io?.upload.addEventListener('progress', (dataEvent) => {
|
||||
if (dataEvent.lengthComputable) {
|
||||
progressUpload(dataEvent.loaded, dataEvent.total);
|
||||
}
|
||||
@ -167,7 +175,7 @@ export function fetchProgress(
|
||||
}
|
||||
// Stream the download progress
|
||||
if (progressDownload) {
|
||||
xhr.io?.addEventListener("progress", (dataEvent) => {
|
||||
xhr.io?.addEventListener('progress', (dataEvent) => {
|
||||
if (dataEvent.lengthComputable) {
|
||||
progressDownload(dataEvent.loaded, dataEvent.total);
|
||||
}
|
||||
@ -187,19 +195,19 @@ export function fetchProgress(
|
||||
};
|
||||
}
|
||||
// Check if we have an internal Fail:
|
||||
xhr.io?.addEventListener("error", () => {
|
||||
xhr.io?.addEventListener('error', () => {
|
||||
xhr.io = undefined;
|
||||
reject(new TypeError("Failed to fetch"));
|
||||
reject(new TypeError('Failed to fetch'));
|
||||
});
|
||||
|
||||
// Capture the end of the stream
|
||||
xhr.io?.addEventListener("loadend", () => {
|
||||
xhr.io?.addEventListener('loadend', () => {
|
||||
if (xhr.io?.readyState !== XMLHttpRequest.DONE) {
|
||||
return;
|
||||
}
|
||||
if (xhr.io?.status === 0) {
|
||||
//the stream has been aborted
|
||||
reject(new TypeError("Fetch has been aborted"));
|
||||
reject(new TypeError('Fetch has been aborted'));
|
||||
return;
|
||||
}
|
||||
// Stream is ended, transform in a generic response:
|
||||
@ -209,17 +217,17 @@ export function fetchProgress(
|
||||
});
|
||||
const headersArray = replaceAll(
|
||||
xhr.io.getAllResponseHeaders().trim(),
|
||||
"\r\n",
|
||||
"\n"
|
||||
).split("\n");
|
||||
'\r\n',
|
||||
'\n'
|
||||
).split('\n');
|
||||
headersArray.forEach(function (header) {
|
||||
const firstColonIndex = header.indexOf(":");
|
||||
const firstColonIndex = header.indexOf(':');
|
||||
if (firstColonIndex !== -1) {
|
||||
const key = header.substring(0, firstColonIndex).trim();
|
||||
const value = header.substring(firstColonIndex + 1).trim();
|
||||
response.headers.set(key, value);
|
||||
} else {
|
||||
response.headers.set(header, "");
|
||||
response.headers.set(header, '');
|
||||
}
|
||||
});
|
||||
xhr.io = undefined;
|
||||
@ -241,27 +249,29 @@ export function RESTRequest({
|
||||
data,
|
||||
params,
|
||||
queries,
|
||||
headers = {},
|
||||
callbacks,
|
||||
}: RESTRequestType): Promise<ModelResponseHttp> {
|
||||
// Create the URL PATH:
|
||||
let generateUrl = RESTUrl({ restModel, restConfig, data, params, queries });
|
||||
let headers: any = {};
|
||||
if (restConfig.token !== undefined && restModel.tokenInUrl !== true) {
|
||||
headers["Authorization"] = `Bearer ${restConfig.token}`;
|
||||
headers['Authorization'] = `Bearer ${restConfig.token}`;
|
||||
}
|
||||
if (restModel.accept !== undefined) {
|
||||
headers["Accept"] = restModel.accept;
|
||||
headers['Accept'] = restModel.accept;
|
||||
}
|
||||
if (restModel.requestType !== HTTPRequestModel.GET &&
|
||||
restModel.requestType !== HTTPRequestModel.ARCHIVE &&
|
||||
restModel.requestType !== HTTPRequestModel.RESTORE
|
||||
if (
|
||||
restModel.requestType !== HTTPRequestModel.GET &&
|
||||
restModel.requestType !== HTTPRequestModel.ARCHIVE &&
|
||||
restModel.requestType !== HTTPRequestModel.RESTORE
|
||||
) {
|
||||
// if Get we have not a content type, the body is empty
|
||||
if (restModel.contentType !== HTTPMimeType.MULTIPART &&
|
||||
restModel.contentType !== undefined
|
||||
) {
|
||||
if (
|
||||
restModel.contentType !== HTTPMimeType.MULTIPART &&
|
||||
restModel.contentType !== undefined
|
||||
) {
|
||||
// special case of multi-part ==> no content type otherwise the browser does not set the ";bundary=--****"
|
||||
headers["Content-Type"] = restModel.contentType;
|
||||
headers['Content-Type'] = restModel.contentType;
|
||||
}
|
||||
}
|
||||
let body = data;
|
||||
@ -302,19 +312,27 @@ export function RESTRequest({
|
||||
}
|
||||
action
|
||||
.then((response: Response) => {
|
||||
if (
|
||||
errorApiGlobalCallback &&
|
||||
400 <= response.status &&
|
||||
response.status <= 499
|
||||
) {
|
||||
// Detect an error and trigger the generic error callback:
|
||||
errorApiGlobalCallback(response);
|
||||
}
|
||||
if (response.status >= 200 && response.status <= 299) {
|
||||
const contentType = response.headers.get("Content-Type");
|
||||
const contentType = response.headers.get('Content-Type');
|
||||
if (
|
||||
!isNullOrUndefined(restModel.accept) &&
|
||||
restModel.accept !== contentType
|
||||
) {
|
||||
reject({
|
||||
name: "Model accept type incompatible",
|
||||
name: 'Model accept type incompatible',
|
||||
time: Date().toString(),
|
||||
status: 901,
|
||||
message: `REST Content type are not compatible: ${restModel.accept} != ${contentType}`,
|
||||
statusMessage: "Fetch error",
|
||||
error: "rest-tools.ts Wrong type in the message return type",
|
||||
statusMessage: 'Fetch error',
|
||||
error: 'rest-tools.ts Wrong type in the message return type',
|
||||
} as RestErrorResponse);
|
||||
} else if (contentType === HTTPMimeType.JSON) {
|
||||
response
|
||||
@ -324,12 +342,12 @@ export function RESTRequest({
|
||||
})
|
||||
.catch((reason: Error) => {
|
||||
reject({
|
||||
name: "API serialization error",
|
||||
name: 'API serialization error',
|
||||
time: Date().toString(),
|
||||
status: 902,
|
||||
message: `REST parse json fail: ${reason}`,
|
||||
statusMessage: "Fetch parse error",
|
||||
error: "rest-tools.ts Wrong message model to parse",
|
||||
statusMessage: 'Fetch parse error',
|
||||
error: 'rest-tools.ts Wrong message model to parse',
|
||||
} as RestErrorResponse);
|
||||
});
|
||||
} else {
|
||||
@ -349,22 +367,22 @@ export function RESTRequest({
|
||||
.text()
|
||||
.then((dataError: string) => {
|
||||
reject({
|
||||
name: "API serialization error",
|
||||
name: 'API serialization error',
|
||||
time: Date().toString(),
|
||||
status: 903,
|
||||
message: `REST parse error json with wrong type fail. ${dataError}`,
|
||||
statusMessage: "Fetch parse error",
|
||||
error: "rest-tools.ts Wrong message model to parse",
|
||||
statusMessage: 'Fetch parse error',
|
||||
error: 'rest-tools.ts Wrong message model to parse',
|
||||
} as RestErrorResponse);
|
||||
})
|
||||
.catch((reason: any) => {
|
||||
reject({
|
||||
name: "API serialization error",
|
||||
name: 'API serialization error',
|
||||
time: Date().toString(),
|
||||
status: response.status,
|
||||
message: `unmanaged error model: ??? with error: ${reason}`,
|
||||
statusMessage: "Fetch ERROR parse error",
|
||||
error: "rest-tools.ts Wrong message model to parse",
|
||||
statusMessage: 'Fetch ERROR parse error',
|
||||
error: 'rest-tools.ts Wrong message model to parse',
|
||||
} as RestErrorResponse);
|
||||
});
|
||||
}
|
||||
@ -374,22 +392,22 @@ export function RESTRequest({
|
||||
.text()
|
||||
.then((dataError: string) => {
|
||||
reject({
|
||||
name: "API serialization error",
|
||||
name: 'API serialization error',
|
||||
time: Date().toString(),
|
||||
status: response.status,
|
||||
message: `unmanaged error model: ${dataError} with error: ${reason}`,
|
||||
statusMessage: "Fetch ERROR TEXT parse error",
|
||||
error: "rest-tools.ts Wrong message model to parse",
|
||||
statusMessage: 'Fetch ERROR TEXT parse error',
|
||||
error: 'rest-tools.ts Wrong message model to parse',
|
||||
} as RestErrorResponse);
|
||||
})
|
||||
.catch((reason: any) => {
|
||||
reject({
|
||||
name: "API serialization error",
|
||||
name: 'API serialization error',
|
||||
time: Date().toString(),
|
||||
status: response.status,
|
||||
message: `unmanaged error model: ??? with error: ${reason}`,
|
||||
statusMessage: "Fetch ERROR TEXT FAIL",
|
||||
error: "rest-tools.ts Wrong message model to parse",
|
||||
statusMessage: 'Fetch ERROR TEXT FAIL',
|
||||
error: 'rest-tools.ts Wrong message model to parse',
|
||||
} as RestErrorResponse);
|
||||
});
|
||||
});
|
||||
@ -400,12 +418,12 @@ export function RESTRequest({
|
||||
reject(error);
|
||||
} else {
|
||||
reject({
|
||||
name: "Request fail",
|
||||
name: 'Request fail',
|
||||
time: Date(),
|
||||
status: 999,
|
||||
message: error,
|
||||
statusMessage: "Fetch catch error",
|
||||
error: "rest-tools.ts detect an error in the fetch request",
|
||||
statusMessage: 'Fetch catch error',
|
||||
error: 'rest-tools.ts detect an error in the fetch request',
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -426,12 +444,12 @@ export function RESTRequestJson<TYPE>(
|
||||
resolve(value.data);
|
||||
} else {
|
||||
reject({
|
||||
name: "Model check fail",
|
||||
name: 'Model check fail',
|
||||
time: Date().toString(),
|
||||
status: 950,
|
||||
error: "REST Fail to verify the data",
|
||||
statusMessage: "API cast ERROR",
|
||||
message: "api.ts Check type as fail",
|
||||
error: 'REST Fail to verify the data',
|
||||
statusMessage: 'API cast ERROR',
|
||||
message: 'api.ts Check type as fail',
|
||||
} as RestErrorResponse);
|
||||
}
|
||||
})
|
||||
|
@ -1,26 +1,61 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
|
||||
|
||||
import { Box, Button, ConditionalValue, Flex, HStack, IconButton, Span, Text, useDisclosure } from '@chakra-ui/react';
|
||||
import { LuAlignJustify, LuArrowBigLeft, LuCircleUserRound, LuKeySquare, LuLogIn, LuLogOut, LuMoon, LuSettings, LuSun } from 'react-icons/lu';
|
||||
import { MdHelp, MdHome, MdMore, MdOutlinePlaylistPlay, MdOutlineUploadFile, MdSupervisedUserCircle } from 'react-icons/md';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
ConditionalValue,
|
||||
Flex,
|
||||
HStack,
|
||||
IconButton,
|
||||
Span,
|
||||
Text,
|
||||
useBreakpointValue,
|
||||
useDisclosure,
|
||||
} from '@chakra-ui/react';
|
||||
import {
|
||||
LuAlignJustify,
|
||||
LuArrowBigLeft,
|
||||
LuCircleUserRound,
|
||||
LuKeySquare,
|
||||
LuLogIn,
|
||||
LuLogOut,
|
||||
LuMoon,
|
||||
LuSettings,
|
||||
LuSun,
|
||||
} from 'react-icons/lu';
|
||||
import {
|
||||
MdHelp,
|
||||
MdHome,
|
||||
MdMore,
|
||||
MdOutlinePlaylistPlay,
|
||||
MdOutlineUploadFile,
|
||||
MdSupervisedUserCircle,
|
||||
} from 'react-icons/md';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
|
||||
|
||||
import { useColorMode, useColorModeValue } from '@/components/ui/color-mode';
|
||||
import { DrawerBody, DrawerContent, DrawerHeader, DrawerRoot } from '@/components/ui/drawer';
|
||||
import { MenuContent, MenuItem, MenuRoot, MenuTrigger } from '@/components/ui/menu';
|
||||
import {
|
||||
DrawerBody,
|
||||
DrawerContent,
|
||||
DrawerHeader,
|
||||
DrawerRoot,
|
||||
} from '@/components/ui/drawer';
|
||||
import {
|
||||
MenuContent,
|
||||
MenuItem,
|
||||
MenuRoot,
|
||||
MenuTrigger,
|
||||
} from '@/components/ui/menu';
|
||||
import { useServiceContext } from '@/service/ServiceContext';
|
||||
import { SessionState } from '@/service/SessionState';
|
||||
import { useSessionService } from '@/service/session';
|
||||
import { colors } from '@/theme/colors';
|
||||
import { requestOpenSite, requestSignIn, requestSignOut, requestSignUp } from '@/utils/sso';
|
||||
|
||||
|
||||
|
||||
|
||||
import {
|
||||
requestOpenSite,
|
||||
requestSignIn,
|
||||
requestSignOut,
|
||||
requestSignUp,
|
||||
} from '@/utils/sso';
|
||||
|
||||
export const TOP_BAR_HEIGHT = '50px';
|
||||
|
||||
@ -37,6 +72,8 @@ export const BUTTON_TOP_BAR_PROPERTY = {
|
||||
export type TopBarProps = {
|
||||
children?: ReactNode;
|
||||
title?: string;
|
||||
titleLink?: string;
|
||||
titleIcon?: ReactNode;
|
||||
};
|
||||
|
||||
const ButtonMenuLeft = ({
|
||||
@ -74,7 +111,12 @@ const ButtonMenuLeft = ({
|
||||
</>
|
||||
);
|
||||
};
|
||||
export const TopBar = ({ title, children }: TopBarProps) => {
|
||||
export const TopBar = ({
|
||||
title,
|
||||
titleLink,
|
||||
titleIcon,
|
||||
children,
|
||||
}: TopBarProps) => {
|
||||
const navigate = useNavigate();
|
||||
const { colorMode, toggleColorMode } = useColorMode();
|
||||
const { session } = useServiceContext();
|
||||
@ -99,8 +141,10 @@ export const TopBar = ({ title, children }: TopBarProps) => {
|
||||
const onKarso = (): void => {
|
||||
requestOpenSite();
|
||||
};
|
||||
const isVisible = useBreakpointValue({ base: false, md: true });
|
||||
return (
|
||||
<Flex
|
||||
minWidth="320px"
|
||||
position="absolute"
|
||||
top={0}
|
||||
left={0}
|
||||
@ -117,21 +161,28 @@ export const TopBar = ({ title, children }: TopBarProps) => {
|
||||
<Button {...BUTTON_TOP_BAR_PROPERTY} onClick={onChangeTheme}>
|
||||
<HStack>
|
||||
<LuAlignJustify />
|
||||
<Text paddingLeft="3px" fontWeight="bold">
|
||||
Karusic
|
||||
</Text>
|
||||
{isVisible && (
|
||||
<Text paddingLeft="3px" fontWeight="bold">
|
||||
Karusic
|
||||
</Text>
|
||||
)}
|
||||
</HStack>
|
||||
</Button>
|
||||
{title && (
|
||||
<Text
|
||||
truncate
|
||||
fontSize="20px"
|
||||
fontWeight="bold"
|
||||
textTransform="uppercase"
|
||||
marginRight="auto"
|
||||
userSelect="none"
|
||||
color="brand.500"
|
||||
onClick={titleLink ? () => navigate(titleLink) : undefined}
|
||||
>
|
||||
{title}
|
||||
<Flex gap="4px">
|
||||
{titleIcon}
|
||||
{title}
|
||||
</Flex>
|
||||
</Text>
|
||||
)}
|
||||
{children}
|
||||
@ -264,4 +315,4 @@ export const TopBar = ({ title, children }: TopBarProps) => {
|
||||
</DrawerRoot>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
@ -9,7 +9,7 @@ import {
|
||||
} from 'react-icons/md';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
import { AlbumResource, AlbumWrite } from '@/back-api';
|
||||
import { AlbumResource, AlbumUpdate } from '@/back-api';
|
||||
import { FormCovers } from '@/components/form/FormCovers';
|
||||
import { FormGroupShow } from '@/components/form/FormGroup';
|
||||
import { FormInput } from '@/components/form/FormInput';
|
||||
@ -64,11 +64,11 @@ export const AlbumEditPopUp = ({}: AlbumEditPopUpProps) => {
|
||||
};
|
||||
const initialRef = useRef<HTMLButtonElement>(null);
|
||||
const finalRef = useRef<HTMLButtonElement>(null);
|
||||
const form = useFormidable<AlbumWrite>({
|
||||
const form = useFormidable<Partial<AlbumUpdate>>({
|
||||
initialValues: dataAlbum,
|
||||
deltaConfig: { omit: ['covers'] },
|
||||
});
|
||||
const onSave = async (deltaData: AlbumWrite) => {
|
||||
const onSave = async (deltaData: Partial<AlbumUpdate>) => {
|
||||
if (isNullOrUndefined(albumIdInt)) {
|
||||
return;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import {
|
||||
} from 'react-icons/md';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
import { ArtistResource, ArtistWrite } from '@/back-api';
|
||||
import { ArtistResource, ArtistUpdate } from '@/back-api';
|
||||
import { FormCovers } from '@/components/form/FormCovers';
|
||||
import { FormInput } from '@/components/form/FormInput';
|
||||
import { FormTextarea } from '@/components/form/FormTextarea';
|
||||
@ -63,11 +63,11 @@ export const ArtistEditPopUp = ({}: ArtistEditPopUpProps) => {
|
||||
};
|
||||
const initialRef = useRef<HTMLButtonElement>(null);
|
||||
const finalRef = useRef<HTMLButtonElement>(null);
|
||||
const form = useFormidable<ArtistWrite>({
|
||||
const form = useFormidable<Partial<ArtistUpdate>>({
|
||||
initialValues: dataArtist,
|
||||
deltaConfig: { omit: ['covers'] },
|
||||
});
|
||||
const onSave = async (dataDelta: ArtistWrite) => {
|
||||
const onSave = async (dataDelta: Partial<ArtistUpdate>) => {
|
||||
if (isNullOrUndefined(artistIdInt)) {
|
||||
return;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import {
|
||||
} from 'react-icons/md';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
import { GenderResource, GenderWrite } from '@/back-api';
|
||||
import { GenderResource, GenderUpdate } from '@/back-api';
|
||||
import { FormCovers } from '@/components/form/FormCovers';
|
||||
import { FormInput } from '@/components/form/FormInput';
|
||||
import { FormTextarea } from '@/components/form/FormTextarea';
|
||||
@ -63,11 +63,11 @@ export const GenderEditPopUp = ({}: GenderEditPopUpProps) => {
|
||||
};
|
||||
const initialRef = useRef<HTMLButtonElement>(null);
|
||||
const finalRef = useRef<HTMLButtonElement>(null);
|
||||
const form = useFormidable<GenderWrite>({
|
||||
const form = useFormidable<Partial<GenderUpdate>>({
|
||||
initialValues: dataGender,
|
||||
deltaConfig: { omit: ['covers'] },
|
||||
});
|
||||
const onSave = async (dataDelta: GenderWrite) => {
|
||||
const onSave = async (dataDelta: Partial<GenderUpdate>) => {
|
||||
if (isNullOrUndefined(genderIdInt)) {
|
||||
return;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import { Button, Text, useDisclosure } from '@chakra-ui/react';
|
||||
import { MdAdminPanelSettings, MdDeleteForever, MdEdit } from 'react-icons/md';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
import { TrackResource, TrackWrite } from '@/back-api';
|
||||
import { TrackResource, TrackUpdate } from '@/back-api';
|
||||
import { FormGroupShow } from '@/components/form/FormGroup';
|
||||
import { FormInput } from '@/components/form/FormInput';
|
||||
import { FormNumber } from '@/components/form/FormNumber';
|
||||
@ -64,11 +64,11 @@ export const TrackEditPopUp = ({}: TrackEditPopUpProps) => {
|
||||
};
|
||||
const initialRef = useRef<HTMLButtonElement>(null);
|
||||
const finalRef = useRef<HTMLButtonElement>(null);
|
||||
const form = useFormidable<TrackWrite>({
|
||||
const form = useFormidable<Partial<TrackUpdate>>({
|
||||
initialValues: dataTrack,
|
||||
deltaConfig: { omit: ['covers'] },
|
||||
});
|
||||
const onSave = async (dataDelta: TrackWrite) => {
|
||||
const onSave = async (dataDelta: Partial<TrackUpdate>) => {
|
||||
if (isNullOrUndefined(trackIdInt)) {
|
||||
return;
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ export const AlbumDetailPage = () => {
|
||||
if (!dataAlbum) {
|
||||
return (
|
||||
<>
|
||||
<TopBar title="Album detail" />
|
||||
<TopBar title="Album detail" titleLink="/album" />
|
||||
<PageLayoutInfoCenter>
|
||||
Fail to load artist id: {albumId}
|
||||
</PageLayoutInfoCenter>
|
||||
@ -54,7 +54,7 @@ export const AlbumDetailPage = () => {
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<TopBar title="Album detail">
|
||||
<TopBar title="Album detail" titleLink="/album">
|
||||
<Button
|
||||
{...BUTTON_TOP_BAR_PROPERTY}
|
||||
onClick={() =>
|
||||
|
@ -28,7 +28,7 @@ export const AlbumsPage = () => {
|
||||
if (isLoading) {
|
||||
return (
|
||||
<>
|
||||
<TopBar title="All Albums" />
|
||||
<TopBar title="All Albums" titleLink="/home" />
|
||||
<PageLayoutInfoCenter>No Album available</PageLayoutInfoCenter>
|
||||
</>
|
||||
);
|
||||
@ -36,7 +36,7 @@ export const AlbumsPage = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopBar title="All Albums">
|
||||
<TopBar title="All Albums" titleLink="/home">
|
||||
<SearchInput onChange={setFilterTitle} />
|
||||
</TopBar>
|
||||
<PageLayout>
|
||||
|
@ -55,33 +55,27 @@ export const ArtistAlbumDetailPage = () => {
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<TopBar title={dataArtist ? undefined : 'Album detail'}>
|
||||
<TopBar
|
||||
title={dataArtist ? dataArtist?.name : 'Album detail'}
|
||||
titleLink={dataArtist ? `/artist/${dataArtist.id}` : undefined}
|
||||
titleIcon={
|
||||
<Covers
|
||||
data={dataArtist?.covers}
|
||||
size="35px"
|
||||
borderRadius="full"
|
||||
iconEmpty={<MdPerson />}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{dataArtist && (
|
||||
<>
|
||||
<Button
|
||||
{...BUTTON_TOP_BAR_PROPERTY}
|
||||
marginRight="auto"
|
||||
onClick={() => navigate(`/artist/${dataArtist.id}`)}
|
||||
>
|
||||
<Covers
|
||||
data={dataArtist?.covers}
|
||||
size="35px"
|
||||
borderRadius="full"
|
||||
iconEmpty={<MdPerson />}
|
||||
/>
|
||||
<Text fontSize="24px" fontWeight="bold">
|
||||
{dataArtist?.name}
|
||||
</Text>
|
||||
</Button>
|
||||
<Button
|
||||
{...BUTTON_TOP_BAR_PROPERTY}
|
||||
onClick={() =>
|
||||
navigate(`/album/${albumId}/edit-album/${dataAlbum.id}`)
|
||||
}
|
||||
>
|
||||
<MdEdit />
|
||||
</Button>
|
||||
</>
|
||||
<Button
|
||||
{...BUTTON_TOP_BAR_PROPERTY}
|
||||
onClick={() =>
|
||||
navigate(`/album/${albumId}/edit-album/${dataAlbum.id}`)
|
||||
}
|
||||
>
|
||||
<MdEdit />
|
||||
</Button>
|
||||
)}
|
||||
</TopBar>
|
||||
<PageLayout>
|
||||
|
@ -32,7 +32,7 @@ export const ArtistDetailPage = () => {
|
||||
if (!dataArtist) {
|
||||
return (
|
||||
<>
|
||||
<TopBar title="Artist detail" />
|
||||
<TopBar title="Artists" titleLink="/artist" />
|
||||
<PageLayoutInfoCenter>
|
||||
Fail to load artist id: {artistId}
|
||||
</PageLayoutInfoCenter>
|
||||
@ -41,19 +41,12 @@ export const ArtistDetailPage = () => {
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<TopBar>
|
||||
<TopBar
|
||||
title="Artists"
|
||||
titleLink="/artist/all"
|
||||
titleIcon={<MdGroup height="full" />}
|
||||
>
|
||||
<>
|
||||
<Button
|
||||
{...BUTTON_TOP_BAR_PROPERTY}
|
||||
marginRight="auto"
|
||||
onClick={() => navigate(`/artist/all`)}
|
||||
>
|
||||
<MdGroup height="full" />
|
||||
<Text fontSize="24px" fontWeight="bold">
|
||||
Artists
|
||||
</Text>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
{...BUTTON_TOP_BAR_PROPERTY}
|
||||
onClick={() =>
|
||||
|
@ -63,7 +63,7 @@ export const ArtistsPage = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopBar title="All artists">
|
||||
<TopBar title="All artists" titleLink="/home">
|
||||
<SearchInput onChange={setFilterName} />
|
||||
<Tooltip.Root aria-label="Random play">
|
||||
<Button {...BUTTON_TOP_BAR_PROPERTY} onClick={onRandomPlay}>
|
||||
|
@ -45,7 +45,7 @@ export const GenderDetailPage = () => {
|
||||
if (!dataGender) {
|
||||
return (
|
||||
<>
|
||||
<TopBar title="Gender detail" />
|
||||
<TopBar title="Gender detail" titleLink="/gender" />
|
||||
<PageLayoutInfoCenter>
|
||||
Fail to load artist id: {genderId}
|
||||
</PageLayoutInfoCenter>
|
||||
@ -54,7 +54,7 @@ export const GenderDetailPage = () => {
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<TopBar title="Gender detail">
|
||||
<TopBar title="Gender detail" titleLink="/gender">
|
||||
<Button
|
||||
{...BUTTON_TOP_BAR_PROPERTY}
|
||||
onClick={() =>
|
||||
|
@ -23,7 +23,7 @@ export const GendersPage = () => {
|
||||
if (isLoading) {
|
||||
return (
|
||||
<>
|
||||
<TopBar title="All Genders" />
|
||||
<TopBar title="All Genders" titleLink="/home" />
|
||||
<PageLayoutInfoCenter>No Gender available</PageLayoutInfoCenter>
|
||||
</>
|
||||
);
|
||||
@ -31,7 +31,7 @@ export const GendersPage = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopBar title="All Genders">
|
||||
<TopBar title="All Genders" titleLink="/home">
|
||||
<SearchInput onChange={setFilterTitle} />
|
||||
</TopBar>
|
||||
<PageLayout>
|
||||
|
@ -97,7 +97,7 @@ export const OnAirPage = () => {
|
||||
if (!playTrackList || playTrackList.length == 0) {
|
||||
return (
|
||||
<>
|
||||
<TopBar title="Album detail" />
|
||||
<TopBar title="On Air ..." titleLink="/home" />
|
||||
<PageLayoutInfoCenter>
|
||||
No data is currently playing...
|
||||
</PageLayoutInfoCenter>
|
||||
@ -106,7 +106,7 @@ export const OnAirPage = () => {
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<TopBar title="On Air ...">
|
||||
<TopBar title="On Air ..." titleLink="/home">
|
||||
<Button {...BUTTON_TOP_BAR_PROPERTY} onClick={clean}>
|
||||
<LuTrash2 />
|
||||
</Button>
|
||||
|
@ -42,7 +42,7 @@ export const TrackSelectionPage = () => {
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<TopBar title="All Tracks" />
|
||||
<TopBar title="All Tracks" titleLink="/home" />
|
||||
<PageLayout>
|
||||
<HStack
|
||||
wrap="wrap"
|
||||
|
@ -1,21 +1,33 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { PartRight, RESTConfig, UserMe, UserResource } from '@/back-api';
|
||||
import { createListCollection } from '@chakra-ui/react';
|
||||
import { useEffectOnce } from 'react-use';
|
||||
|
||||
import {
|
||||
PartRight,
|
||||
RESTConfig,
|
||||
UserMe,
|
||||
UserResource,
|
||||
setErrorApiGlobalCallback,
|
||||
} from '@/back-api';
|
||||
import { environment, getApiUrl } from '@/environment';
|
||||
import { useServiceContext } from '@/service/ServiceContext';
|
||||
import { SessionState } from '@/service/SessionState';
|
||||
import { isBrowser } from '@/utils/layout';
|
||||
import { parseToken } from '@/utils/sso';
|
||||
import { createListCollection } from '@chakra-ui/react';
|
||||
|
||||
const TOKEN_KEY = 'karusic-token-key-storage';
|
||||
|
||||
export const USERS_COLLECTION = createListCollection({
|
||||
items: [
|
||||
{ label: "admin", value: "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIxIiwiYXBwbGljYXRpb24iOiJrYXJ1c2ljIiwiaXNzIjoiS2FyQXV0aCIsInJpZ2h0Ijp7ImthcnVzaWMiOnsiQURNSU4iOnRydWUsIlVTRVIiOnRydWV9fSwibG9naW4iOiJIZWVyb1l1aSIsImV4cCI6MTcyNDIwNjc5NCwiaWF0IjoxNzI0MTY2ODM0fQ.TEST_SIGNATURE_FOR_LOCAL_TEST_AND_TEST_E2E" },
|
||||
{ label: "NO_USER", value: "svelte" },
|
||||
{
|
||||
label: 'admin',
|
||||
value:
|
||||
'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIxIiwiYXBwbGljYXRpb24iOiJrYXJ1c2ljIiwiaXNzIjoiS2FyQXV0aCIsInJpZ2h0Ijp7ImthcnVzaWMiOnsiQURNSU4iOnRydWUsIlVTRVIiOnRydWV9fSwibG9naW4iOiJIZWVyb1l1aSIsImV4cCI6MTcyNDIwNjc5NCwiaWF0IjoxNzI0MTY2ODM0fQ.TEST_SIGNATURE_FOR_LOCAL_TEST_AND_TEST_E2E',
|
||||
},
|
||||
{ label: 'NO_USER', value: 'svelte' },
|
||||
],
|
||||
})
|
||||
});
|
||||
export const USERS = {
|
||||
admin:
|
||||
'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIxIiwiYXBwbGljYXRpb24iOiJrYXJ1c2ljIiwiaXNzIjoiS2FyQXV0aCIsInJpZ2h0Ijp7ImthcnVzaWMiOnsiQURNSU4iOnRydWUsIlVTRVIiOnRydWV9fSwibG9naW4iOiJIZWVyb1l1aSIsImV4cCI6MTcyNDIwNjc5NCwiaWF0IjoxNzI0MTY2ODM0fQ.TEST_SIGNATURE_FOR_LOCAL_TEST_AND_TEST_E2E',
|
||||
@ -57,20 +69,26 @@ export const useSessionServiceWrapped = (): SessionServiceProps => {
|
||||
const [state, setState] = useState<SessionState>(SessionState.NO_USER);
|
||||
const [config, setConfig] = useState<UserMe | undefined>(undefined);
|
||||
|
||||
useEffectOnce(() => {
|
||||
setErrorApiGlobalCallback((response: Response) => {
|
||||
if (response.status == 401) {
|
||||
console.error('Detect 401 error ==> remove token');
|
||||
clearToken();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const updateRight = useCallback(() => {
|
||||
console.log('update right...');
|
||||
if (isBrowser) {
|
||||
console.log('Detect a new token...');
|
||||
setState(SessionState.NO_USER);
|
||||
setConfig(undefined);
|
||||
|
||||
if (token === undefined) {
|
||||
console.log(` ==> No User`);
|
||||
setState(SessionState.NO_USER);
|
||||
setConfig(undefined);
|
||||
localStorage.removeItem(TOKEN_KEY);
|
||||
} else if (token === '__LOGOUT__') {
|
||||
console.log(` ==> disconnection: ${token}`);
|
||||
setState(SessionState.DISCONNECT);
|
||||
localStorage.removeItem(TOKEN_KEY);
|
||||
} else if (!['__LOGOUT__', '__FAIL__', '__CANCEL__'].includes(token)) {
|
||||
} else {
|
||||
console.log(' ==> Login ... (try to keep right)');
|
||||
setState(SessionState.CONNECTING);
|
||||
localStorage.setItem(TOKEN_KEY, token);
|
||||
@ -84,6 +102,7 @@ export const useSessionServiceWrapped = (): SessionServiceProps => {
|
||||
})
|
||||
.catch((error) => {
|
||||
setState(SessionState.CONNECTION_FAIL);
|
||||
setConfig(undefined);
|
||||
//console.log(` ==> Fail to get right: '${error}'`);
|
||||
localStorage.removeItem(TOKEN_KEY);
|
||||
});
|
||||
@ -91,7 +110,10 @@ export const useSessionServiceWrapped = (): SessionServiceProps => {
|
||||
}
|
||||
}, [localStorage, parseToken, token]);
|
||||
const setTokenLocal = useCallback(
|
||||
(token: string) => {
|
||||
(token?: string) => {
|
||||
if (token ? token.startsWith('__') : false) {
|
||||
token = undefined;
|
||||
}
|
||||
setToken(token);
|
||||
updateRight();
|
||||
},
|
||||
|
File diff suppressed because one or more lines are too long
@ -5,9 +5,6 @@ import { defineConfig } from 'vite';
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
define: {
|
||||
'process.env.NODE_ENV': '"development"', // Forcer le mode dev pour éviter l'erreur
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src'),
|
||||
|
Loading…
x
Reference in New Issue
Block a user