Compare commits
7 Commits
lab06(Vue)
...
lab6(MVC)
| Author | SHA1 | Date | |
|---|---|---|---|
| 2eaf84e681 | |||
| 322353ff16 | |||
| b6b676717c | |||
| e86f9c2d5a | |||
| dccbe55510 | |||
| cdbbe467d0 | |||
| d12e8a5f61 |
45
build.gradle
45
build.gradle
@@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '3.0.2'
|
||||
id 'org.springframework.boot' version '2.6.3'
|
||||
id 'io.spring.dependency-management' version '1.1.0'
|
||||
}
|
||||
|
||||
@@ -13,15 +13,50 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||
implementation 'org.springframework.boot:spring-boot-devtools'
|
||||
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
|
||||
|
||||
implementation 'org.webjars:bootstrap:5.1.3'
|
||||
implementation 'org.webjars:jquery:3.6.0'
|
||||
implementation 'org.webjars:font-awesome:6.1.0'
|
||||
|
||||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||
implementation 'com.h2database:h2:2.1.210'
|
||||
implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.5'
|
||||
implementation 'org.jetbrains:annotations:24.0.0'
|
||||
implementation 'org.jetbrains:annotations:24.0.0'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||
|
||||
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
|
||||
|
||||
implementation 'org.hibernate.validator:hibernate-validator'
|
||||
|
||||
implementation 'org.springdoc:springdoc-openapi-ui:1.6.5'
|
||||
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
|
||||
// implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
// implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||
// implementation 'com.h2database:h2:2.1.210'
|
||||
// implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.5'
|
||||
//
|
||||
// implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||
// implementation 'org.springframework.boot:spring-boot-devtools'
|
||||
// implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
|
||||
// implementation 'org.webjars:bootstrap:5.1.3'
|
||||
// implementation 'org.webjars:jquery:3.6.0'
|
||||
// implementation 'org.webjars:font-awesome:6.1.0'
|
||||
//
|
||||
// implementation 'org.jetbrains:annotations:24.0.0'
|
||||
// implementation 'org.jetbrains:annotations:24.0.0'
|
||||
// implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||
// implementation 'org.hibernate.validator:hibernate-validator'
|
||||
// testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
//
|
||||
// implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||
// implementation 'com.auth0:java-jwt:4.4.0'
|
||||
// implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
|
||||
// annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
|
||||
}
|
||||
|
||||
tasks.named('test') {
|
||||
|
||||
@@ -7,9 +7,6 @@ import ru.ulstu.is.sbapp.database.model.Album;
|
||||
import java.util.List;
|
||||
|
||||
public interface IAlbumRepository extends JpaRepository<Album, Long> {
|
||||
@Query("select a.albumName as album, s.songName as songs " +
|
||||
"from Album a " +
|
||||
"join a.songs s " +
|
||||
"group by a.id, a.albumName, s.songName")
|
||||
List<Object[]> getAll();
|
||||
@Query("SELECT a FROM Album a WHERE a.albumName = :name")
|
||||
List<Album> getAlbumsByName(String name);
|
||||
}
|
||||
|
||||
@@ -11,4 +11,6 @@ import java.util.List;
|
||||
public interface IArtistRepository extends JpaRepository<Artist, Long> {
|
||||
@Query(value = "SELECT * FROM artist_album", nativeQuery = true)
|
||||
List<Object[]> getAllArtistAlbum();
|
||||
@Query("SELECT a FROM Artist a WHERE a.artistName = :name")
|
||||
List<Artist> getArtistsByName(String name);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import ru.ulstu.is.sbapp.database.model.Song;
|
||||
import java.util.List;
|
||||
|
||||
public interface ISongRepository extends JpaRepository<Song, Long> {
|
||||
@Query("SELECT s.songs FROM Album s WHERE :song MEMBER OF s.songs")
|
||||
@Query("SELECT a.songs FROM Album a WHERE :song MEMBER OF a.songs")
|
||||
List<Song> findSongsInAlbum(@Param("song") Song song);
|
||||
@Query("SELECT s FROM Song s WHERE s.songName = :name")
|
||||
List<Song> getSongsByName(String name);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package ru.ulstu.is.sbapp.Repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import ru.ulstu.is.sbapp.database.model.User;
|
||||
|
||||
public interface IUserRepository extends JpaRepository<User, Long> {
|
||||
User findOneByLoginIgnoreCase(String login);
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package ru.ulstu.is.sbapp;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
class WebConfiguration implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**").allowedMethods("*");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package ru.ulstu.is.sbapp.configuration;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
@Configuration
|
||||
public class PasswordEncoderConfiguration {
|
||||
@Bean
|
||||
public PasswordEncoder createPasswordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package ru.ulstu.is.sbapp.configuration;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import ru.ulstu.is.sbapp.controllers.UserSignUpMvcController;
|
||||
import ru.ulstu.is.sbapp.database.model.UserRole;
|
||||
import ru.ulstu.is.sbapp.database.service.UserService;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(securedEnabled = true)
|
||||
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||
private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class);
|
||||
private static final String LOGIN_URL = "/login";
|
||||
private final UserService userService;
|
||||
public SecurityConfiguration(UserService userService) {
|
||||
this.userService = userService;
|
||||
createAdminOnStartup();
|
||||
}
|
||||
private void createAdminOnStartup() {
|
||||
final String admin = "admin";
|
||||
if (userService.findByLogin(admin) == null) {
|
||||
log.info("Admin user successfully created");
|
||||
userService.createUser(admin, admin, admin, UserRole.ADMIN);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.headers().frameOptions().sameOrigin().and()
|
||||
.cors().and()
|
||||
.csrf().disable()
|
||||
.authorizeRequests()
|
||||
.antMatchers(UserSignUpMvcController.SIGNUP_URL).permitAll()
|
||||
.antMatchers(HttpMethod.GET, LOGIN_URL).permitAll()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.formLogin()
|
||||
.loginPage(LOGIN_URL).permitAll()
|
||||
.and()
|
||||
.logout().permitAll();
|
||||
}
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.userDetailsService(userService);
|
||||
}
|
||||
@Override
|
||||
public void configure(WebSecurity web) {
|
||||
web.ignoring()
|
||||
.antMatchers("/css/**")
|
||||
.antMatchers("/js/**")
|
||||
.antMatchers("/templates/**")
|
||||
.antMatchers("/webjars/**");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package ru.ulstu.is.sbapp.configuration;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class WebConfiguration implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**").allowedMethods("*");
|
||||
}
|
||||
public static final String REST_API = "/api";
|
||||
@Override
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
WebMvcConfigurer.super.addViewControllers(registry);
|
||||
registry.addViewController("login");
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,21 @@
|
||||
package ru.ulstu.is.sbapp.controllers;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import javax.validation.Valid;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import ru.ulstu.is.sbapp.configuration.WebConfiguration;
|
||||
import ru.ulstu.is.sbapp.database.model.Artist;
|
||||
import ru.ulstu.is.sbapp.database.model.Song;
|
||||
import ru.ulstu.is.sbapp.database.model.UserRole;
|
||||
import ru.ulstu.is.sbapp.database.service.AlbumService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/album")
|
||||
@RequestMapping(WebConfiguration.REST_API + "/album")
|
||||
public class AlbumController {
|
||||
private final AlbumService albumService;
|
||||
|
||||
@@ -32,6 +36,7 @@ public class AlbumController {
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@PreAuthorize("hasAuthority('ROLE_ADMIN')")
|
||||
public AlbumDTO createAlbum(@RequestBody @Valid AlbumDTO albumDTO){
|
||||
return new AlbumDTO(albumService.addAlbum(albumDTO.getAlbumName()));
|
||||
}
|
||||
@@ -78,8 +83,4 @@ public class AlbumController {
|
||||
public void addArtistToAlbum(@PathVariable Long id, @RequestBody @Valid List<Long> artistIds){
|
||||
albumService.addArtistToAlbum(id, artistIds);
|
||||
}
|
||||
@GetMapping("/getAll")
|
||||
public Map<String, List<String>> getAll(){
|
||||
return albumService.getAll();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
package ru.ulstu.is.sbapp.controllers;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import ru.ulstu.is.sbapp.database.model.Artist;
|
||||
import ru.ulstu.is.sbapp.database.model.Song;
|
||||
import ru.ulstu.is.sbapp.database.service.AlbumService;
|
||||
import ru.ulstu.is.sbapp.database.service.ArtistService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/album")
|
||||
public class AlbumMvcController {
|
||||
private final AlbumService albumService;
|
||||
private final ArtistService artistService;
|
||||
|
||||
public AlbumMvcController(AlbumService albumService, ArtistService artistService)
|
||||
{
|
||||
this.albumService = albumService;
|
||||
this.artistService = artistService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String getAlbums(Model model, Authentication authentication) {
|
||||
model.addAttribute("albums",
|
||||
albumService.findAllAlbums().stream()
|
||||
.map(AlbumDTO::new)
|
||||
.toList());
|
||||
boolean isAdmin = authentication.getAuthorities().stream()
|
||||
.anyMatch(authority -> authority.getAuthority().equals("ROLE_ADMIN"));
|
||||
model.addAttribute("isAdmin", isAdmin);
|
||||
return "album";
|
||||
}
|
||||
|
||||
@GetMapping(value = {"/edit", "/edit/{id}"})
|
||||
public String editAlbum(@PathVariable(required = false) Long id,
|
||||
Model model, Authentication authentication) {
|
||||
if (id == null || id <= 0) {
|
||||
model.addAttribute("albumDTO", new AlbumDTO());
|
||||
boolean isAdmin = authentication.getAuthorities().stream()
|
||||
.anyMatch(authority -> authority.getAuthority().equals("ROLE_ADMIN"));
|
||||
model.addAttribute("isAdmin", isAdmin);
|
||||
} else {
|
||||
model.addAttribute("albumId", id);
|
||||
model.addAttribute("albumDTO", new AlbumDTO(albumService.findAlbum(id)));
|
||||
}
|
||||
return "album-edit";
|
||||
}
|
||||
|
||||
@PostMapping(value = {"/", "/{id}"})
|
||||
public String saveAlbum(@PathVariable(required = false) Long id,
|
||||
@ModelAttribute @Valid AlbumDTO albumDTO,
|
||||
BindingResult bindingResult,
|
||||
Model model) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
model.addAttribute("errors", bindingResult.getAllErrors());
|
||||
return "album-edit";
|
||||
}
|
||||
if (id == null || id <= 0) {
|
||||
albumService.addAlbum(albumDTO.getAlbumName());
|
||||
} else {
|
||||
albumService.updateAlbum(id, albumDTO.getAlbumName());
|
||||
}
|
||||
return "redirect:/album";
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
public String deleteAlbum(@PathVariable Long id) {
|
||||
albumService.deleteAlbum(id);
|
||||
return "redirect:/album";
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/songs/{id}")
|
||||
public String getSongsFromAlbum(@PathVariable Long id, Model model) {
|
||||
List<Song> songs = albumService.getSongFromAlbum(id);
|
||||
model.addAttribute("songs", songs);
|
||||
return "view-songs";
|
||||
}
|
||||
|
||||
@GetMapping("/artists/{id}")
|
||||
public String getArtistsFromAlbum(@PathVariable Long id, Model model) {
|
||||
List<Artist> artists = albumService.getArtistInAlbum(id);
|
||||
model.addAttribute("artists", artists);
|
||||
return "view-artists";
|
||||
}
|
||||
|
||||
@GetMapping("/getSongsUndefined/{id}")
|
||||
public String getSongsFromUndefinedAlbum(@PathVariable Long id, Model model){
|
||||
List<Song> songs = albumService.getSongsUndefined();
|
||||
model.addAttribute("undefinedSongs", songs);
|
||||
model.addAttribute("albumId", id);
|
||||
model.addAttribute("albumDTO", new AlbumDTO(albumService.findAlbum(id)));
|
||||
return "add-songs-to-album";
|
||||
}
|
||||
|
||||
@PostMapping("/addSongs/{id}")
|
||||
public String addSongToAlbum(@PathVariable Long id,
|
||||
@RequestParam("songId") List<String> songsIds){
|
||||
List<Long> songIdsAsLong = songsIds.stream()
|
||||
.map(Long::parseLong)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
albumService.addSongToAlbum(id, songIdsAsLong);
|
||||
return "redirect:/album";
|
||||
}
|
||||
|
||||
@PostMapping("/deleteSongFromAlbum/{id}")
|
||||
public String deleteSongFromAlbum(@PathVariable Long id) {
|
||||
albumService.deleteSongFromAlbum(id);
|
||||
return "redirect:/album";
|
||||
}
|
||||
|
||||
@GetMapping("/getArtistsUndefined/{id}")
|
||||
public String getArtistsFromUndefinedAlbum(@PathVariable Long id, Model model){
|
||||
List<Artist> artists = albumService.getArtistsUndefined(id);
|
||||
model.addAttribute("undefinedArtists", artists);
|
||||
model.addAttribute("albumId", id);
|
||||
model.addAttribute("albumDTO", new AlbumDTO(albumService.findAlbum(id)));
|
||||
return "add-artist-to-album";
|
||||
}
|
||||
|
||||
@PostMapping("/addArtists/{id}")
|
||||
public String addArtistToAlbum(@PathVariable Long id,
|
||||
@RequestParam("artistId") List<String> artistIds){
|
||||
List<Long> artistIdsAsLong = artistIds.stream()
|
||||
.map(Long::parseLong)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
albumService.addArtistToAlbum(id, artistIdsAsLong);
|
||||
return "redirect:/album";
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,18 @@
|
||||
package ru.ulstu.is.sbapp.controllers;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import javax.validation.Valid;
|
||||
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import ru.ulstu.is.sbapp.configuration.WebConfiguration;
|
||||
import ru.ulstu.is.sbapp.database.model.UserRole;
|
||||
import ru.ulstu.is.sbapp.database.service.ArtistService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/artist")
|
||||
@RequestMapping(WebConfiguration.REST_API + "/artist")
|
||||
public class ArtistController {
|
||||
private final ArtistService artistService;
|
||||
|
||||
@@ -28,6 +33,7 @@ public class ArtistController {
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@PreAuthorize("hasAuthority('ROLE_ADMIN')")
|
||||
public ArtistDTO createArtist(@RequestBody @Valid ArtistDTO artistDTO){
|
||||
return new ArtistDTO(artistService.addArtist(artistDTO.getArtistName(), artistDTO.getGenre()));
|
||||
}
|
||||
@@ -48,7 +54,7 @@ public class ArtistController {
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/addArtistToAlbum")
|
||||
public void addArtistToAlbum(@PathVariable Long id, @RequestBody @Valid List<Long> groupsIds){
|
||||
artistService.addArtistToAlbum(id, groupsIds);
|
||||
public void addArtistToAlbum(@PathVariable Long id, @RequestBody @Valid List<Long> albumsIds){
|
||||
artistService.addArtistToAlbum(id, albumsIds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,9 @@ public class ArtistDTO {
|
||||
public void setGenre(String genre){
|
||||
this.genre = genre;
|
||||
}
|
||||
public void setArtistName(String artistName){
|
||||
this.artistName = artistName;
|
||||
}
|
||||
|
||||
public List<Long> getAlbumIds(){
|
||||
return albumIds;
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package ru.ulstu.is.sbapp.controllers;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import ru.ulstu.is.sbapp.database.service.AlbumService;
|
||||
import ru.ulstu.is.sbapp.database.service.ArtistService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/artist")
|
||||
public class ArtistMvcController {
|
||||
private final ArtistService artistService;
|
||||
|
||||
private final AlbumService albumService;
|
||||
|
||||
public ArtistMvcController(ArtistService artistService, AlbumService albumService)
|
||||
{
|
||||
this.artistService = artistService;
|
||||
this.albumService = albumService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String getArtists(Model model, Authentication authentication) {
|
||||
model.addAttribute("artists",
|
||||
artistService.findAllArtists().stream()
|
||||
.map(ArtistDTO::new)
|
||||
.toList());
|
||||
boolean isAdmin = authentication.getAuthorities().stream()
|
||||
.anyMatch(authority -> authority.getAuthority().equals("ROLE_ADMIN"));
|
||||
model.addAttribute("isAdmin", isAdmin);
|
||||
return "artist";
|
||||
}
|
||||
|
||||
@GetMapping(value = {"/edit", "/edit/{id}"})
|
||||
public String editArtist(@PathVariable(required = false) Long id,
|
||||
Model model, Authentication authentication) {
|
||||
if (id == null || id <= 0) {
|
||||
model.addAttribute("artistDTO", new ArtistDTO());
|
||||
boolean isAdmin = authentication.getAuthorities().stream()
|
||||
.anyMatch(authority -> authority.getAuthority().equals("ROLE_ADMIN"));
|
||||
model.addAttribute("isAdmin", isAdmin);
|
||||
} else {
|
||||
model.addAttribute("artistId", id);
|
||||
model.addAttribute("artistDTO", new ArtistDTO(artistService.findArtist(id)));
|
||||
}
|
||||
return "artist-edit";
|
||||
}
|
||||
|
||||
@PostMapping(value = {"/", "/{id}"})
|
||||
public String saveArtist(@PathVariable(required = false) Long id,
|
||||
@ModelAttribute @Valid ArtistDTO artistDTO,
|
||||
BindingResult bindingResult,
|
||||
Model model) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
model.addAttribute("errors", bindingResult.getAllErrors());
|
||||
return "artist-edit";
|
||||
}
|
||||
if (id == null || id <= 0) {
|
||||
artistService.addArtist(artistDTO.getArtistName(), artistDTO.getGenre());
|
||||
} else {
|
||||
artistService.updateArtist(id, artistDTO.getArtistName(), artistDTO.getGenre());
|
||||
}
|
||||
return "redirect:/artist";
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
public String deleteArtist(@PathVariable Long id) {
|
||||
artistService.deleteArtist(id);
|
||||
return "redirect:/artist";
|
||||
}
|
||||
|
||||
@GetMapping("/addArtistToAlbum/{id}")
|
||||
public String addArtistToAlbumForm(@PathVariable Long id, Model model) {
|
||||
model.addAttribute("artistDTO", new ArtistDTO(artistService.findArtist(id)));
|
||||
model.addAttribute("artistId", id);
|
||||
model.addAttribute("albums", albumService.findAllAlbums());
|
||||
return "add-artist-to-album";
|
||||
}
|
||||
|
||||
@PostMapping("/addArtistToAlbum/{id}")
|
||||
public String addArtistToAlbum(@PathVariable Long id,
|
||||
@RequestParam("albumId") List<Long> albumIds) {
|
||||
artistService.addArtistToAlbum(id, albumIds);
|
||||
return "redirect:/artist";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package ru.ulstu.is.sbapp.controllers;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import ru.ulstu.is.sbapp.configuration.WebConfiguration;
|
||||
import ru.ulstu.is.sbapp.database.service.SearchService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(WebConfiguration.REST_API + "/search")
|
||||
public class SearchController {
|
||||
private final SearchService searchService;
|
||||
|
||||
public SearchController(SearchService searchService) {
|
||||
this.searchService = searchService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public Map<String, List<Object>> getByName(@RequestParam(value = "name", defaultValue = "песня") String name) {
|
||||
return searchService.getByName(name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package ru.ulstu.is.sbapp.controllers;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import ru.ulstu.is.sbapp.database.service.SearchService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/search")
|
||||
public class SearchMvcController {
|
||||
private final SearchService searchService;
|
||||
public SearchMvcController(SearchService searchService) {
|
||||
this.searchService = searchService;
|
||||
}
|
||||
@GetMapping
|
||||
public String getByName(@RequestParam(value = "name", defaultValue = "песня") String name, Model model) {
|
||||
Map<String, List<Object>> searchResult = searchService.getByName(name);
|
||||
model.addAttribute("name", name);
|
||||
model.addAttribute("searchResult", searchResult != null);
|
||||
if (searchResult != null) {
|
||||
model.addAttribute("songs", searchResult.get("songs"));
|
||||
model.addAttribute("albums", searchResult.get("albums"));
|
||||
model.addAttribute("artists", searchResult.get("artists"));
|
||||
}
|
||||
return "search";
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,19 @@
|
||||
package ru.ulstu.is.sbapp.controllers;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import javax.validation.Valid;
|
||||
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import ru.ulstu.is.sbapp.configuration.WebConfiguration;
|
||||
import ru.ulstu.is.sbapp.database.model.UserRole;
|
||||
import ru.ulstu.is.sbapp.database.service.AlbumService;
|
||||
import ru.ulstu.is.sbapp.database.service.SongService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/song")
|
||||
@RequestMapping(WebConfiguration.REST_API + "/song")
|
||||
public class SongController {
|
||||
private final SongService songService;
|
||||
private final AlbumService albumService;
|
||||
@@ -31,6 +36,7 @@ public class SongController {
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@PreAuthorize("hasAuthority('ROLE_ADMIN')")
|
||||
public SongDTO createSong(@RequestBody @Valid SongDTO songDTO){
|
||||
return new SongDTO(songService.addSong(songDTO.getSongName(), songDTO.getDuration()));
|
||||
}
|
||||
@@ -51,5 +57,4 @@ public class SongController {
|
||||
.map(AlbumDTO::new)
|
||||
.toList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -39,11 +39,11 @@ public class SongDTO {
|
||||
return albumId;
|
||||
}
|
||||
|
||||
public void setAlbumId(long id){
|
||||
public void setAlbumId(Long id){
|
||||
this.albumId = id;
|
||||
}
|
||||
|
||||
public void setId(long id){
|
||||
public void setId(Long id){
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package ru.ulstu.is.sbapp.controllers;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import ru.ulstu.is.sbapp.database.service.AlbumService;
|
||||
import ru.ulstu.is.sbapp.database.service.SongService;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/song")
|
||||
public class SongMvcController {
|
||||
private final SongService songService;
|
||||
private final AlbumService albumService;
|
||||
|
||||
public SongMvcController(SongService songService, AlbumService albumService)
|
||||
{
|
||||
this.songService = songService;
|
||||
this.albumService = albumService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String getSongs(Model model, Authentication authentication) {
|
||||
model.addAttribute("songs",
|
||||
songService.findAllSongs().stream()
|
||||
.map(SongDTO::new)
|
||||
.toList());
|
||||
boolean isAdmin = authentication.getAuthorities().stream()
|
||||
.anyMatch(authority -> authority.getAuthority().equals("ROLE_ADMIN"));
|
||||
model.addAttribute("isAdmin", isAdmin);
|
||||
return "song";
|
||||
}
|
||||
|
||||
@GetMapping(value = {"/edit", "/edit/{id}"})
|
||||
public String editSong(@PathVariable(required = false) Long id,
|
||||
Model model, Authentication authentication) {
|
||||
model.addAttribute("Albums", albumService.findAllAlbums());
|
||||
if (id == null || id <= 0) {
|
||||
model.addAttribute("songDTO", new SongDTO());
|
||||
boolean isAdmin = authentication.getAuthorities().stream()
|
||||
.anyMatch(authority -> authority.getAuthority().equals("ROLE_ADMIN"));
|
||||
model.addAttribute("isAdmin", isAdmin);
|
||||
} else {
|
||||
model.addAttribute("songId", id);
|
||||
model.addAttribute("songDTO", new SongDTO(songService.findSong(id)));
|
||||
}
|
||||
return "song-edit";
|
||||
}
|
||||
|
||||
@PostMapping(value = {"/", "/{id}"})
|
||||
public String saveSong(@PathVariable(required = false) Long id,
|
||||
@ModelAttribute @Valid SongDTO songDTO,
|
||||
BindingResult bindingResult,
|
||||
Model model) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
model.addAttribute("errors", bindingResult.getAllErrors());
|
||||
return "song-edit";
|
||||
}
|
||||
if (id == null || id <= 0) {
|
||||
songService.addSong(songDTO.getSongName(), songDTO.getDuration());
|
||||
} else {
|
||||
songService.updateSong(id, songDTO.getSongName(), songDTO.getDuration());
|
||||
}
|
||||
return "redirect:/song";
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
public String deleteSong(@PathVariable Long id) {
|
||||
songService.deleteSong(id);
|
||||
return "redirect:/song";
|
||||
}
|
||||
}
|
||||
28
src/main/java/ru/ulstu/is/sbapp/controllers/UserDTO.java
Normal file
28
src/main/java/ru/ulstu/is/sbapp/controllers/UserDTO.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package ru.ulstu.is.sbapp.controllers;
|
||||
|
||||
import ru.ulstu.is.sbapp.database.model.User;
|
||||
import ru.ulstu.is.sbapp.database.model.UserRole;
|
||||
|
||||
public class UserDTO {
|
||||
private final long id;
|
||||
private final String login;
|
||||
private final UserRole role;
|
||||
|
||||
public UserDTO(User user) {
|
||||
this.id = user.getId();
|
||||
this.login = user.getLogin();
|
||||
this.role = user.getRole();
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getLogin() {
|
||||
return login;
|
||||
}
|
||||
|
||||
public UserRole getRole() {
|
||||
return role;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package ru.ulstu.is.sbapp.controllers;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import ru.ulstu.is.sbapp.database.model.UserRole;
|
||||
import ru.ulstu.is.sbapp.database.service.UserService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/users")
|
||||
public class UserMvcController {
|
||||
private final UserService userService;
|
||||
|
||||
public UserMvcController(UserService userService) {
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@Secured({UserRole.AsString.ADMIN})
|
||||
public String getUsers(@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "5") int size,
|
||||
Model model) {
|
||||
final Page<UserDTO> users = userService.findAllPages(page, size)
|
||||
.map(UserDTO::new);
|
||||
model.addAttribute("users", users);
|
||||
final int totalPages = users.getTotalPages();
|
||||
final List<Integer> pageNumbers = IntStream.rangeClosed(1, totalPages)
|
||||
.boxed()
|
||||
.toList();
|
||||
model.addAttribute("pages", pageNumbers);
|
||||
model.addAttribute("totalPages", totalPages);
|
||||
return "users";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package ru.ulstu.is.sbapp.controllers;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
public class UserSignUpDTO {
|
||||
@NotBlank
|
||||
@Size(min = 3, max = 64)
|
||||
private String login;
|
||||
@NotBlank
|
||||
@Size(min = 6, max = 64)
|
||||
private String password;
|
||||
@NotBlank
|
||||
@Size(min = 6, max = 64)
|
||||
private String passwordConfirm;
|
||||
|
||||
public String getLogin() {
|
||||
return login;
|
||||
}
|
||||
|
||||
public void setLogin(String login) {
|
||||
this.login = login;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getPasswordConfirm() {
|
||||
return passwordConfirm;
|
||||
}
|
||||
|
||||
public void setPasswordConfirm(String passwordConfirm) {
|
||||
this.passwordConfirm = passwordConfirm;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package ru.ulstu.is.sbapp.controllers;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import ru.ulstu.is.sbapp.database.model.User;
|
||||
import ru.ulstu.is.sbapp.database.service.UserService;
|
||||
import ru.ulstu.is.sbapp.database.util.validation.ValidationException;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(UserSignUpMvcController.SIGNUP_URL)
|
||||
public class UserSignUpMvcController {
|
||||
public static final String SIGNUP_URL = "/signup";
|
||||
private final UserService userService;
|
||||
|
||||
public UserSignUpMvcController(UserService userService) {
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String showSignupForm(Model model) {
|
||||
model.addAttribute("UserDTO", new UserSignUpDTO());
|
||||
return "signup";
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public String signup(@ModelAttribute("UserDTO") @Valid UserSignUpDTO userSignupDto,
|
||||
BindingResult bindingResult,
|
||||
Model model) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
model.addAttribute("errors", bindingResult.getAllErrors());
|
||||
return "signup";
|
||||
}
|
||||
try {
|
||||
final User user = userService.createUser(userSignupDto.getLogin(), userSignupDto.getPassword(), userSignupDto.getPasswordConfirm());
|
||||
return "redirect:/login?created=" + user.getLogin();
|
||||
} catch (ValidationException e) {
|
||||
model.addAttribute("errors", e.getMessage());
|
||||
return "signup";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package ru.ulstu.is.sbapp.database.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonManagedReference;
|
||||
import jakarta.persistence.*;
|
||||
import javax.persistence.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -12,12 +12,12 @@ import java.util.Objects;
|
||||
@Table(name = "albums")
|
||||
public class Album {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
private String albumName;
|
||||
|
||||
@JsonManagedReference
|
||||
@OneToMany(fetch = FetchType.EAGER, mappedBy = "album")
|
||||
@OneToMany(cascade = CascadeType.MERGE, mappedBy = "album")
|
||||
private List<Song> songs;
|
||||
|
||||
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||
@@ -26,6 +26,9 @@ public class Album {
|
||||
inverseJoinColumns = {@JoinColumn(name = "artist_id")})
|
||||
private List<Artist> artists;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name= "user_id", nullable = false)
|
||||
private User user;
|
||||
public Album() {
|
||||
this.songs = new ArrayList<>();
|
||||
}
|
||||
@@ -39,6 +42,12 @@ public class Album {
|
||||
public String getAlbumName() { return albumName; }
|
||||
public void setAlbumName(String name) { this.albumName = name; }
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package ru.ulstu.is.sbapp.database.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonBackReference;
|
||||
import jakarta.persistence.*;
|
||||
import javax.persistence.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -12,7 +12,7 @@ import java.util.Objects;
|
||||
@Table(name = "artists")
|
||||
public class Artist {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
private String artistName;
|
||||
private String genre;
|
||||
@@ -23,6 +23,10 @@ public class Artist {
|
||||
inverseJoinColumns = {@JoinColumn(name = "album_id")})
|
||||
private List<Album> albums = new ArrayList<>();
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name= "user_id", nullable = false)
|
||||
private User user;
|
||||
|
||||
public Artist() {
|
||||
}
|
||||
|
||||
@@ -37,6 +41,13 @@ public class Artist {
|
||||
public String getGenre() { return genre; }
|
||||
public void setGenre(String genre) { this.genre = genre; }
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package ru.ulstu.is.sbapp.database.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonBackReference;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import javax.persistence.*;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -11,7 +9,7 @@ import java.util.Objects;
|
||||
@Table(name = "songs")
|
||||
public class Song {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
private String songName;
|
||||
private Double duration;
|
||||
@@ -21,6 +19,10 @@ public class Song {
|
||||
@JoinColumn(name = "album_id", nullable = true)
|
||||
private Album album;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name= "user_id", nullable = false)
|
||||
private User user;
|
||||
|
||||
public Song() {
|
||||
}
|
||||
|
||||
@@ -35,6 +37,13 @@ public class Song {
|
||||
public Double getDuration() { return duration;}
|
||||
public void setDuration(Double duration) { this.duration = duration; }
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
||||
83
src/main/java/ru/ulstu/is/sbapp/database/model/User.java
Normal file
83
src/main/java/ru/ulstu/is/sbapp/database/model/User.java
Normal file
@@ -0,0 +1,83 @@
|
||||
package ru.ulstu.is.sbapp.database.model;
|
||||
|
||||
import javax.persistence.*;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@Entity
|
||||
@Table(name = "users")
|
||||
public class User {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
@Column(nullable = false, unique = true, length = 64)
|
||||
@NotBlank
|
||||
@Size(min = 3, max = 64)
|
||||
private String login;
|
||||
@Column(nullable = false, length = 64)
|
||||
@NotBlank
|
||||
@Size(min = 6, max = 64)
|
||||
private String password;
|
||||
private UserRole role;
|
||||
|
||||
public User() {
|
||||
}
|
||||
|
||||
public User(String login, String password) {
|
||||
this(login, password, UserRole.USER);
|
||||
}
|
||||
|
||||
public User(String login, String password, UserRole role) {
|
||||
this.login = login;
|
||||
this.password = password;
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getLogin() {
|
||||
return login;
|
||||
}
|
||||
|
||||
public void setLogin(String login) {
|
||||
this.login = login;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public UserRole getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
User user = (User) o;
|
||||
return Objects.equals(id, user.id) && Objects.equals(login, user.login);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, login);
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "User{" +
|
||||
"id=" + id +
|
||||
", login='" + login + '\'' +
|
||||
", password='" + password + '\'' +
|
||||
", role='" + role + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
20
src/main/java/ru/ulstu/is/sbapp/database/model/UserRole.java
Normal file
20
src/main/java/ru/ulstu/is/sbapp/database/model/UserRole.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package ru.ulstu.is.sbapp.database.model;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
public enum UserRole implements GrantedAuthority {
|
||||
ADMIN,
|
||||
USER;
|
||||
|
||||
private static final String PREFIX = "ROLE_";
|
||||
|
||||
@Override
|
||||
public String getAuthority() {
|
||||
return PREFIX + this.name();
|
||||
}
|
||||
|
||||
public static final class AsString {
|
||||
public static final String ADMIN = PREFIX + "ADMIN";
|
||||
public static final String USER = PREFIX + "USER";
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
package ru.ulstu.is.sbapp.database.service;
|
||||
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import ru.ulstu.is.sbapp.Repository.IAlbumRepository;
|
||||
import ru.ulstu.is.sbapp.database.model.Album;
|
||||
import ru.ulstu.is.sbapp.database.model.Artist;
|
||||
import ru.ulstu.is.sbapp.database.model.Song;
|
||||
import ru.ulstu.is.sbapp.database.model.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -21,11 +22,13 @@ public class AlbumService {
|
||||
|
||||
private final SongService songService;
|
||||
private final ArtistService artistService;
|
||||
private final UserService userService;
|
||||
|
||||
public AlbumService(IAlbumRepository albumRepository, SongService songService, @Lazy ArtistService artistService) {
|
||||
public AlbumService(IAlbumRepository albumRepository, SongService songService, @Lazy ArtistService artistService, UserService userService) {
|
||||
this.albumRepository = albumRepository;
|
||||
this.songService = songService;
|
||||
this.artistService = artistService;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -34,6 +37,18 @@ public class AlbumService {
|
||||
throw new IllegalArgumentException("Album name is null or empty");
|
||||
}
|
||||
final Album album = new Album(name);
|
||||
Object currentUser = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||
if(currentUser instanceof UserDetails){
|
||||
String username = ((UserDetails)currentUser).getUsername();
|
||||
User user = userService.findByLogin(username);
|
||||
if(user.getRole() == UserRole.ADMIN){
|
||||
album.setUser(user);
|
||||
} else {
|
||||
throw new AccessDeniedException("User does not have permission to perform this operation");
|
||||
}
|
||||
} else {
|
||||
throw new AccessDeniedException("Authentication required for this operation");
|
||||
}
|
||||
return albumRepository.save(album);
|
||||
}
|
||||
|
||||
@@ -128,13 +143,13 @@ public class AlbumService {
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Map<String, List<String>> getAll(){
|
||||
return albumRepository.getAll().stream()
|
||||
.collect(
|
||||
Collectors.groupingBy(
|
||||
o -> (String) o[0],
|
||||
Collectors.mapping( o -> (String) o[1], Collectors.toList() )
|
||||
)
|
||||
);
|
||||
public List<Artist> getArtistsUndefined(Long albumId){
|
||||
List<Artist> artists = new ArrayList<>();
|
||||
for(Artist artist : artistService.findAllArtists()){
|
||||
if(!artist.getAlbumIds().contains(albumId)){
|
||||
artists.add(artist);
|
||||
}
|
||||
}
|
||||
return artists;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package ru.ulstu.is.sbapp.database.service;
|
||||
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import ru.ulstu.is.sbapp.Repository.IArtistRepository;
|
||||
import ru.ulstu.is.sbapp.database.model.Artist;
|
||||
import ru.ulstu.is.sbapp.database.model.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -14,10 +17,12 @@ import java.util.Optional;
|
||||
public class ArtistService {
|
||||
private final IArtistRepository artistRepository;
|
||||
private final AlbumService albumService;
|
||||
private final UserService userService;
|
||||
|
||||
public ArtistService(IArtistRepository artistRepository, AlbumService albumService) {
|
||||
public ArtistService(IArtistRepository artistRepository, AlbumService albumService, UserService userService) {
|
||||
this.artistRepository = artistRepository;
|
||||
this.albumService = albumService;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -26,6 +31,18 @@ public class ArtistService {
|
||||
throw new IllegalArgumentException("Artist name or genre is null or empty");
|
||||
}
|
||||
final Artist artist = new Artist(artistName, genre);
|
||||
Object currentUser = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||
if(currentUser instanceof UserDetails){
|
||||
String username = ((UserDetails)currentUser).getUsername();
|
||||
User user = userService.findByLogin(username);
|
||||
if(user.getRole() == UserRole.ADMIN){
|
||||
artist.setUser(user);
|
||||
} else {
|
||||
throw new AccessDeniedException("User does not have permission to perform this operation");
|
||||
}
|
||||
} else {
|
||||
throw new AccessDeniedException("Authentication required for this operation");
|
||||
}
|
||||
return artistRepository.save(artist);
|
||||
}
|
||||
|
||||
@@ -47,13 +64,14 @@ public class ArtistService {
|
||||
}
|
||||
final Artist currentArtist = findArtist(id);
|
||||
currentArtist.setArtistName(name);
|
||||
currentArtist.setGenre(genre);
|
||||
return artistRepository.save(currentArtist);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Artist deleteArtist(Long id) {
|
||||
final Artist currentArtist = findArtist(id);
|
||||
currentArtist.getAlbums().clear(); // Удаляем все связи с альбомами
|
||||
currentArtist.getAlbums().clear();
|
||||
artistRepository.delete(currentArtist);
|
||||
return currentArtist;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package ru.ulstu.is.sbapp.database.service;
|
||||
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import ru.ulstu.is.sbapp.Repository.IAlbumRepository;
|
||||
import ru.ulstu.is.sbapp.Repository.IArtistRepository;
|
||||
import ru.ulstu.is.sbapp.Repository.ISongRepository;
|
||||
import ru.ulstu.is.sbapp.database.model.Album;
|
||||
import ru.ulstu.is.sbapp.database.model.Artist;
|
||||
import ru.ulstu.is.sbapp.database.model.Song;
|
||||
import ru.ulstu.is.sbapp.database.model.User;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class SearchService {
|
||||
|
||||
private final IAlbumRepository albumRepository;
|
||||
private final ISongRepository songRepository;
|
||||
private final IArtistRepository artistRepository;
|
||||
private final UserService userService;
|
||||
|
||||
|
||||
public SearchService(IAlbumRepository albumRepository, ISongRepository songRepository, IArtistRepository artistRepository, UserService userService) {
|
||||
this.albumRepository = albumRepository;
|
||||
this.songRepository = songRepository;
|
||||
this.artistRepository = artistRepository;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Map<String, List<Object>> getByName(String name) {
|
||||
Map<String, List<Object>> resultMap = new HashMap<>();
|
||||
|
||||
List<Song> songs = songRepository.getSongsByName(name).stream().toList();
|
||||
List<Object> songsResult = new ArrayList<>(songs);
|
||||
resultMap.put("songs", songsResult);
|
||||
|
||||
List<Album> albums = albumRepository.getAlbumsByName(name).stream().toList();
|
||||
List<Object> albumsResult = new ArrayList<>(albums);
|
||||
resultMap.put("albums", albumsResult);
|
||||
|
||||
List<Artist> artists = artistRepository.getArtistsByName(name).stream().toList();
|
||||
List<Object> artistsResult = new ArrayList<>(artists);
|
||||
resultMap.put("artists", artistsResult);
|
||||
|
||||
return resultMap;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
package ru.ulstu.is.sbapp.database.service;
|
||||
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import ru.ulstu.is.sbapp.Repository.ISongRepository;
|
||||
import ru.ulstu.is.sbapp.database.model.Song;
|
||||
import ru.ulstu.is.sbapp.database.model.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -15,18 +18,32 @@ public class SongService {
|
||||
|
||||
private final ISongRepository songRepository;
|
||||
private final AlbumService albumService;
|
||||
private final UserService userService;
|
||||
|
||||
public SongService(ISongRepository songRepository, @Lazy AlbumService albumService) {
|
||||
public SongService(ISongRepository songRepository, @Lazy AlbumService albumService, UserService userService) {
|
||||
this.songRepository = songRepository;
|
||||
this.albumService = albumService;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Song addSong(String songName, Double duration){
|
||||
if (!StringUtils.hasText(songName) || !StringUtils.hasText(String.valueOf(duration))) {
|
||||
if (!StringUtils.hasText(songName) || duration == null) {
|
||||
throw new IllegalArgumentException("Song name or duration is null or empty");
|
||||
}
|
||||
final Song song = new Song(songName, duration);
|
||||
Object currentUser = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||
if(currentUser instanceof UserDetails){
|
||||
String username = ((UserDetails)currentUser).getUsername();
|
||||
User user = userService.findByLogin(username);
|
||||
if(user.getRole() == UserRole.ADMIN){
|
||||
song.setUser(user);
|
||||
} else {
|
||||
throw new AccessDeniedException("User does not have permission to perform this operation");
|
||||
}
|
||||
} else {
|
||||
throw new AccessDeniedException("Authentication required for this operation");
|
||||
}
|
||||
return songRepository.save(song);
|
||||
}
|
||||
|
||||
@@ -43,7 +60,7 @@ public class SongService {
|
||||
|
||||
@Transactional
|
||||
public Song updateSong(Long id, String name, Double duration) {
|
||||
if (!StringUtils.hasText(name) || !StringUtils.hasText(String.valueOf(duration))) {
|
||||
if (!StringUtils.hasText(name) || duration == null) {
|
||||
throw new IllegalArgumentException("Song name or duration is null or empty");
|
||||
}
|
||||
final Song currentSong = findSong(id);
|
||||
@@ -63,15 +80,4 @@ public class SongService {
|
||||
public void deleteAllSong() {
|
||||
songRepository.deleteAll();
|
||||
}
|
||||
|
||||
// @Transactional
|
||||
// public void AddSongToAlbum(Long idSong, Long idAlbum){
|
||||
// Song song = findSong(idSong);
|
||||
// Album album = em.find(Album.class, idAlbum);
|
||||
// if (album == null || song == null) {
|
||||
// throw new EntityNotFoundException("Album or Song not found");
|
||||
// }
|
||||
// song.setAlbum(album);
|
||||
// em.merge(song);
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package ru.ulstu.is.sbapp.database.service;
|
||||
|
||||
import javax.validation.ValidationException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.ulstu.is.sbapp.Repository.IUserRepository;
|
||||
import ru.ulstu.is.sbapp.database.model.User;
|
||||
import ru.ulstu.is.sbapp.database.model.UserRole;
|
||||
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
|
||||
@Service
|
||||
public class UserService implements UserDetailsService {
|
||||
private final IUserRepository userRepository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
public UserService(IUserRepository userRepository,
|
||||
PasswordEncoder passwordEncoder) {
|
||||
this.userRepository = userRepository;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
public Page<User> findAllPages(int page, int size) {
|
||||
return userRepository.findAll(PageRequest.of(page - 1, size, Sort.by("id").ascending()));
|
||||
}
|
||||
public User findByLogin(String login) {
|
||||
return userRepository.findOneByLoginIgnoreCase(login);
|
||||
}
|
||||
public User createUser(String login, String password, String passwordConfirm) {
|
||||
return createUser(login, password, passwordConfirm, UserRole.USER);
|
||||
}
|
||||
public User createUser(String login, String password, String passwordConfirm, UserRole role) {
|
||||
if (findByLogin(login) != null) {
|
||||
throw new ValidationException(String.format("User '%s' already exists", login));
|
||||
}
|
||||
final User user = new User(login, passwordEncoder.encode(password), role);
|
||||
if (!Objects.equals(password, passwordConfirm)) {
|
||||
throw new ValidationException("Passwords not equals");
|
||||
}
|
||||
return userRepository.save(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
final User userEntity = findByLogin(username);
|
||||
if (userEntity == null) {
|
||||
throw new UsernameNotFoundException(username);
|
||||
}
|
||||
return new org.springframework.security.core.userdetails.User(
|
||||
userEntity.getLogin(), userEntity.getPassword(), Collections.singleton(userEntity.getRole()));
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import ru.ulstu.is.sbapp.database.service.AlbumNotFoundException;
|
||||
import ru.ulstu.is.sbapp.database.service.ArtistNotFoundException;
|
||||
import ru.ulstu.is.sbapp.database.service.SongNotFoundException;
|
||||
@@ -13,7 +14,7 @@ import ru.ulstu.is.sbapp.database.util.validation.ValidationException;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ControllerAdvice
|
||||
@ControllerAdvice(annotations = RestController.class)
|
||||
public class AdviceController {
|
||||
@ExceptionHandler({
|
||||
AlbumNotFoundException.class,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package ru.ulstu.is.sbapp.database.util.validation;
|
||||
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.Validation;
|
||||
import jakarta.validation.Validator;
|
||||
import jakarta.validation.ValidatorFactory;
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.Validation;
|
||||
import javax.validation.Validator;
|
||||
import javax.validation.ValidatorFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
37
src/main/resources/templates/add-artist-to-album.html
Normal file
37
src/main/resources/templates/add-artist-to-album.html
Normal file
@@ -0,0 +1,37 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<div layout:fragment="content">
|
||||
<div th:text="${errors}" class="margin-bottom alert-danger"></div>
|
||||
<form action="#" th:action="@{/album/addArtists/{id}(id=${albumId})}" method="post">
|
||||
<div class="mb-3">
|
||||
<label for="albumName" class="form-label">Название</label>
|
||||
<input type="text" class="form-control" id="albumName" th:field="${albumDTO.albumName}" readonly>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="albumName">Выберите исполнителей для добавления:</label>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item" th:each="artist, iterator: ${undefinedArtists}">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="artistId" th:value="${artist.id}">
|
||||
<label class="form-check-label" th:text="${artist.artistName}"></label>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<button type="submit" class="btn btn-primary button-fixed">
|
||||
<span>Добавить</span>
|
||||
</button>
|
||||
<a class="btn btn-secondary button-fixed" th:href="@{/album}">
|
||||
Назад
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
37
src/main/resources/templates/add-songs-to-album.html
Normal file
37
src/main/resources/templates/add-songs-to-album.html
Normal file
@@ -0,0 +1,37 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<div layout:fragment="content">
|
||||
<div th:text="${errors}" class="margin-bottom alert-danger"></div>
|
||||
<form action="#" th:action="@{/album/addSongs/{id}(id=${albumId})}" method="post">
|
||||
<div class="mb-3">
|
||||
<label for="albumName" class="form-label">Название</label>
|
||||
<input type="text" class="form-control" id="albumName" th:field="${albumDTO.albumName}" readonly>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="albumName">Выберите песни:</label>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item" th:each="song, iterator: ${undefinedSongs}">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="songId" th:value="${song.id}">
|
||||
<label class="form-check-label" th:text="${song.songName}"></label>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<button type="submit" class="btn btn-primary button-fixed">
|
||||
<span>Добавить</span>
|
||||
</button>
|
||||
<a class="btn btn-secondary button-fixed" th:href="@{/album}">
|
||||
Назад
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
27
src/main/resources/templates/album-edit.html
Normal file
27
src/main/resources/templates/album-edit.html
Normal file
@@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<div layout:fragment="content">
|
||||
<div th:text="${errors}" class="margin-bottom alert-danger"></div>
|
||||
<form action="#" th:action="@{/album/{id}(id=${id})}" th:object="${albumDTO}" method="post">
|
||||
<div class="mb-3">
|
||||
<label for="albumName" class="form-label">Название</label>
|
||||
<input type="text" class="form-control" id="albumName" th:field="${albumDTO.albumName}" required="true">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<button type="submit" class="btn btn-primary button-fixed">
|
||||
<span th:if="${albumId == null}">Добавить</span>
|
||||
<span th:if="${albumId != null}">Обновить</span>
|
||||
</button>
|
||||
<a class="btn btn-secondary button-fixed" th:href="@{/album}">
|
||||
Назад
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
56
src/main/resources/templates/album.html
Normal file
56
src/main/resources/templates/album.html
Normal file
@@ -0,0 +1,56 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml">
|
||||
<body>
|
||||
<div layout:fragment="content">
|
||||
<h1 class="text-center mb-4">Альбомы</h1>
|
||||
<div>
|
||||
<a class="btn btn-success button-fixed"
|
||||
th:href="@{/album/edit}" th:if="${isAdmin}">
|
||||
<i class="fa-solid fa-plus"></i> Добавить
|
||||
</a>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Название</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="album, iterator: ${albums}">
|
||||
<td th:text="${album.albumName}"></td>
|
||||
<td>
|
||||
<div class="btn-album" role="album" aria-label="Basic example">
|
||||
<a class="btn btn-warning button-fixed button-sm"
|
||||
th:href="@{/album/edit/{id}(id=${album.id})}" th:if="${isAdmin}">
|
||||
<i class="fa fa-pencil" aria-hidden="true"></i> Изменить
|
||||
</a>
|
||||
<button type="button" class="btn btn-danger button-fixed button-sm"
|
||||
th:attr="onclick=|confirm('Удалить запись?') && document.getElementById('remove-${album.id}').click()|" th:if="${isAdmin}">
|
||||
<i class="fa fa-trash" aria-hidden="true"></i> Удалить
|
||||
</button>
|
||||
<a class="btn btn-primary button-fixed button-sm"
|
||||
th:href="@{/album/songs/{id}(id=${album.id})}">Посмотреть песни</a>
|
||||
<a class="btn btn-primary button-fixed button-sm"
|
||||
th:href="@{/album/getSongsUndefined/{id}(id=${album.id})}">Добавить песни</a>
|
||||
<a class="btn btn-primary button-fixed button-sm"
|
||||
th:href="@{/album/getArtistsUndefined/{id}(id=${album.id})}"> Добавить исполнителей</a>
|
||||
<a class="btn btn-primary button-fixed button-sm"
|
||||
th:href="@{/album/artists/{id}(id=${album.id})}">Посмотреть исполнителей</a>
|
||||
</div>
|
||||
<form th:action="@{/album/delete/{id}(id=${album.id})}" method="post" th:if="${isAdmin}">
|
||||
<button th:id="'remove-' + ${album.id}" type="submit" style="display: none">
|
||||
Удалить
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
29
src/main/resources/templates/artist-edit.html
Normal file
29
src/main/resources/templates/artist-edit.html
Normal file
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<div layout:fragment="content">
|
||||
<div th:text="${errors}" class="margin-bottom alert-danger"></div>
|
||||
<form action="#" th:action="@{/artist/{id}(id=${id})}" th:object="${artistDTO}" method="post">
|
||||
<div class="mb-3">
|
||||
<label for="artistName" class="form-label">Имя</label>
|
||||
<input type="text" class="form-control" id="artistName" th:field="${artistDTO.artistName}" required="true">
|
||||
<label for="genre" class="form-label">Жанр</label>
|
||||
<input type="text" class="form-control" id="genre" th:field="${artistDTO.genre}" required="true">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<button type="submit" class="btn btn-primary button-fixed">
|
||||
<span th:if="${artistId == null}">Добавить</span>
|
||||
<span th:if="${artistId != null}">Обновить</span>
|
||||
</button>
|
||||
<a class="btn btn-secondary button-fixed" th:href="@{/artist}">
|
||||
Назад
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
50
src/main/resources/templates/artist.html
Normal file
50
src/main/resources/templates/artist.html
Normal file
@@ -0,0 +1,50 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml">
|
||||
<body>
|
||||
<div layout:fragment="content">
|
||||
<h1 class="text-center mb-4">Исполнители</h1>
|
||||
<div>
|
||||
<a class="btn btn-success button-fixed"
|
||||
th:href="@{/artist/edit}" th:if="${isAdmin}">
|
||||
<i class="fa-solid fa-plus"></i> Добавить
|
||||
</a>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Имя</th>
|
||||
<th scope="col">Жанр</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="artist, iterator: ${artists}">
|
||||
<td th:text="${artist.artistName}"></td>
|
||||
<td th:text="${artist.genre}"></td>
|
||||
<td>
|
||||
<div class="btn-album" role="group" aria-label="Basic example">
|
||||
<a class="btn btn-warning button-fixed button-sm"
|
||||
th:href="@{/artist/edit/{id}(id=${artist.id})}" th:if="${isAdmin}">
|
||||
<i class="fa fa-pencil" aria-hidden="true"></i> Изменить
|
||||
</a>
|
||||
<button type="button" class="btn btn-danger button-fixed button-sm"
|
||||
th:attr="onclick=|confirm('Удалить запись?') && document.getElementById('remove-${artist.id}').click()|" th:if="${isAdmin}">
|
||||
<i class="fa fa-trash" aria-hidden="true"></i> Удалить
|
||||
</button>
|
||||
</div>
|
||||
<form th:action="@{/artist/delete/{id}(id=${artist.id})}" method="post" th:if="${isAdmin}">
|
||||
<button th:id="'remove-' + ${artist.id}" type="submit" style="display: none">
|
||||
Удалить
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
42
src/main/resources/templates/default.html
Normal file
42
src/main/resources/templates/default.html
Normal file
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
xmlns:th="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Streaming Service</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<link rel="icon" href="/favicon.svg">
|
||||
<script type="text/javascript" src="/webjars/bootstrap/5.1.3/js/bootstrap.bundle.min.js"></script>
|
||||
<link rel="stylesheet" href="/webjars/bootstrap/5.1.3/css/bootstrap.min.css"/>
|
||||
<link rel="stylesheet" href="/webjars/font-awesome/6.1.0/css/all.min.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||
<div class="container-fluid">
|
||||
<button class="navbar-toggler" type="button"
|
||||
data-bs-toggle="collapse" data-bs-target="#navbarNav"
|
||||
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav">
|
||||
<a class="nav-link" href="/">Стриминговый сервис</a>
|
||||
<a class="nav-link" href="/song" th:classappend="${#strings.equals(activeLink, '/song')} ? 'active' : ''">Песни</a>
|
||||
<a class="nav-link" href="/album" th:classappend="${#strings.equals(activeLink, '/album')} ? 'active' : ''">Альбомы</a>
|
||||
<a class="nav-link" href="/artist" th:classappend="${#strings.equals(activeLink, '/artist')} ? 'active' : ''">Исполнители</a>
|
||||
<a class="nav-link" href="/search" th:classappend="${#strings.equals(activeLink, '/search')} ? 'active' : ''">Поиск</a>
|
||||
<a class="nav-link" href="/users" th:classappend="${#strings.equals(activeLink, '/users')} ? 'active' : ''">Пользователи</a>
|
||||
<a class="nav-link" href="/logout" th:classappend="${#strings.equals(activeLink, '/login')} ? 'active' : ''">Выход</a>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container-fluid">
|
||||
<div class="container container-padding" layout:fragment="content">
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<th:block layout:fragment="scripts">
|
||||
</th:block>
|
||||
</html>
|
||||
13
src/main/resources/templates/error.html
Normal file
13
src/main/resources/templates/error.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml">
|
||||
<body>
|
||||
<div class="container" layout:fragment="content">
|
||||
<div class="alert alert-danger">
|
||||
<span th:text="${error}"></span>
|
||||
</div>
|
||||
<a href="/">На главную</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
13
src/main/resources/templates/index.html
Normal file
13
src/main/resources/templates/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
layout:decorate="~{default}">
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<div layout:fragment="content">
|
||||
<div>It's works!</div>
|
||||
<a href="123">ERROR</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
42
src/main/resources/templates/login.html
Normal file
42
src/main/resources/templates/login.html
Normal file
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
xmlns:th="http://www.w3.org/1999/xhtml"
|
||||
layout:decorate="~{default}">
|
||||
<body>
|
||||
<div layout:fragment="content">
|
||||
<div th:if="${param.error}" class="alert alert-danger margin-bottom">
|
||||
Пользователь не найден
|
||||
</div>
|
||||
<div th:if="${param.logout}" class="alert alert-success margin-bottom">
|
||||
Выход произошёл успешно
|
||||
</div>
|
||||
<div th:if="${param.created}" class="alert alert-success margin-bottom">
|
||||
Пользователь '<span th:text="${param.created}"></span>' успешно создан
|
||||
</div>
|
||||
<form th:action="@{/login}" method="post">
|
||||
<div class="mb-3">
|
||||
<p class="mb-1">Логин</p>
|
||||
<input name="username" id="username" class="form-control"
|
||||
type="text" required autofocus />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<p class="mb-1">Пароль</p>
|
||||
<input name="password" id="password" class="form-control"
|
||||
type="password" required />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<button type="submit" class="btn btn-success">
|
||||
Войти
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<span>Еще не зарегистрированы?</span>
|
||||
<a href="/signup">Зарегистрироваться</a>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
42
src/main/resources/templates/search.html
Normal file
42
src/main/resources/templates/search.html
Normal file
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml">
|
||||
<body>
|
||||
<div layout:fragment="content">
|
||||
<form th:action="@{/search}" method="get">
|
||||
<div class="search-box">
|
||||
<label for="name"><h2>Введите имя для поиска:</h2></label>
|
||||
<input type="text" class="form-control" id="name" th:name="name" th:value="${name}" required>
|
||||
<br>
|
||||
<div>
|
||||
<button type="submit" class="btn btn-primary" >Поиск</button>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div>
|
||||
<h2>Результат поиска "<span th:text="${name}"></span>"</h2>
|
||||
<div th:if="searchResult">
|
||||
<h3>Песни</h3>
|
||||
<ul>
|
||||
<li th:each="song : ${songs}">
|
||||
<span th:text="${song.songName}"></span>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>Альбомы</h3>
|
||||
<ul>
|
||||
<li th:each="album : ${albums}">
|
||||
<span th:text="${album.albumName}"></span>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>Исполнители</h3>
|
||||
<ul>
|
||||
<li th:each="artist : ${artists}">
|
||||
<span th:text="${artist.artistName}"></span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
29
src/main/resources/templates/signup.html
Normal file
29
src/main/resources/templates/signup.html
Normal file
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
xmlns:th="http://www.thymeleaf.org"
|
||||
layout:decorate="~{default}">
|
||||
<body>
|
||||
<div class="container container-padding mt-3" layout:fragment="content">
|
||||
<div th:if="${errors}" th:text="${errors}" class="margin-bottom alert alert-danger"></div>
|
||||
<form action="#" th:action="@{/signup}" th:object="${UserDTO}" method="post">
|
||||
<div class="mb-3">
|
||||
<input type="text" class="form-control" th:field="${UserDTO.login}"
|
||||
placeholder="Логин" required="true" autofocus="true" maxlength="64"/>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<input type="password" class="form-control" th:field="${UserDTO.password}"
|
||||
placeholder="Пароль" required="true" minlength="6" maxlength="64"/>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<input type="password" class="form-control" th:field="${UserDTO.passwordConfirm}"
|
||||
placeholder="Пароль (подтверждение)" required="true" minlength="6" maxlength="64"/>
|
||||
</div>
|
||||
<div class="mx-4">
|
||||
<button type="submit" class="btn btn-success button-fixed">Создать</button>
|
||||
<a class="btn btn-primary button-fixed" href="/login">Назад</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
29
src/main/resources/templates/song-edit.html
Normal file
29
src/main/resources/templates/song-edit.html
Normal file
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<div layout:fragment="content">
|
||||
<div th:text="${errors}" class="margin-bottom alert-danger"></div>
|
||||
<form action="#" th:action="@{/song/{id}(id=${id})}" th:object="${songDTO}" method="post">
|
||||
<div class="mb-3">
|
||||
<label for="songName" class="form-label">Название</label>
|
||||
<input type="text" class="form-control" id="songName" th:field="${songDTO.songName}" required>
|
||||
<label for="duration" class="form-label">Продолжительность</label>
|
||||
<input type="number" step="0.1" class="form-control" id="duration" th:field="${songDTO.duration}" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<button type="submit" class="btn btn-primary button-fixed">
|
||||
<span th:if="${songId == null}">Добавить</span>
|
||||
<span th:if="${songId != null}">Обновить</span>
|
||||
</button>
|
||||
<a class="btn btn-secondary button-fixed" th:href="@{/song}">
|
||||
Назад
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
54
src/main/resources/templates/song.html
Normal file
54
src/main/resources/templates/song.html
Normal file
@@ -0,0 +1,54 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<div layout:fragment="content">
|
||||
<h1 class="text-center mb-4">Песни</h1>
|
||||
<div>
|
||||
<a class="btn btn-success button-fixed"
|
||||
th:href="@{/song/edit}" th:if="${isAdmin}">
|
||||
<i class="fa-solid fa-plus"></i> Добавить
|
||||
</a>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Название</th>
|
||||
<th scope="col">Продолжительность</th>
|
||||
<th scope="col">Альбом</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="song, iterator: ${songs}">
|
||||
<td th:text="${song.songName}"></td>
|
||||
<td th:text="${song.duration}"></td>
|
||||
<td th:text="${song.albumName}"></td>
|
||||
<td>
|
||||
<div class="btn-album" role="group" aria-label="Basic example">
|
||||
<a class="btn btn-warning button-fixed button-sm"
|
||||
th:href="@{/song/edit/{id}(id=${song.id})}" th:if="${isAdmin}">
|
||||
<i class="fa fa-pencil" aria-hidden="true"></i> Изменить
|
||||
</a>
|
||||
<button type="button" class="btn btn-danger button-fixed button-sm"
|
||||
th:attr="onclick=|confirm('Удалить запись?') && document.getElementById('remove-${song.id}').click()|" th:if="${isAdmin}">
|
||||
<i class="fa fa-trash" aria-hidden="true"></i> Удалить
|
||||
</button>
|
||||
</div>
|
||||
<form th:action="@{/song/delete/{id}(id=${song.id})}" method="post" th:if="${isAdmin}">
|
||||
<button th:id="'remove-' + ${song.id}" type="submit" style="display: none">
|
||||
Удалить
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
38
src/main/resources/templates/users.html
Normal file
38
src/main/resources/templates/users.html
Normal file
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
xmlns:th="http://www.thymeleaf.org"
|
||||
layout:decorate="~{default}">
|
||||
<body>
|
||||
<div class="container" layout:fragment="content">
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">№</th>
|
||||
<th scope="col">ID</th>
|
||||
<th scope="col">Логин</th>
|
||||
<th scope="col">Роль</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="user, iterator: ${users}">
|
||||
<th scope="row" th:text="${iterator.index} + 1"></th>
|
||||
<td th:text="${user.id}"></td>
|
||||
<td th:text="${user.login}" style="width: 60%"></td>
|
||||
<td th:text="${user.role}" style="width: 20%"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div th:if="${totalPages > 0}" class="pagination">
|
||||
<span style="float: left;">Страницы:</span>
|
||||
<a th:each="page : ${pages}"
|
||||
th:href="@{/users(page=${page}, size=${users.size})}"
|
||||
th:text="${page}"
|
||||
th:class="${page == users.number + 1} ? active">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
30
src/main/resources/templates/view-artists.html
Normal file
30
src/main/resources/templates/view-artists.html
Normal file
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<div layout:fragment="content">
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Имя</th>
|
||||
<th scope="col">Жанр</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="artist, iterator: ${artists}">
|
||||
<td th:text="${artist.artistName}"></td>
|
||||
<td th:text="${artist.genre}"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<a class="btn btn-secondary button-fixed" th:href="@{/album}">
|
||||
Назад
|
||||
</a>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
36
src/main/resources/templates/view-songs.html
Normal file
36
src/main/resources/templates/view-songs.html
Normal file
@@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<div layout:fragment="content">
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Название</th>
|
||||
<th scope="col">Продолжительность</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="song, iterator: ${songs}">
|
||||
<td th:text="${song.songName}"></td>
|
||||
<td th:text="${song.duration}"></td>
|
||||
<td>
|
||||
<form method="post" th:action="@{/album/deleteSongFromAlbum/{id}(id=${song.id})}">
|
||||
<button type="submit" class="btn btn-danger button-fixed button-sm">Удалить</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<a class="btn btn-secondary button-fixed" th:href="@{/album}">
|
||||
Назад
|
||||
</a>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user