Initial commit
This commit is contained in:
@ -0,0 +1,13 @@
|
||||
package com.bivashy.backend.composer;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class ComposerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ComposerApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
package com.bivashy.backend.composer.auth;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
public class CustomUserDetails implements UserDetails {
|
||||
private final long id;
|
||||
private final String username;
|
||||
private final String password;
|
||||
|
||||
public CustomUserDetails(long id, String username, String password) {
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
// TODO: Authority implementation
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package com.bivashy.backend.composer.config;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
|
||||
@Configuration
|
||||
public class CORSConfig {
|
||||
@Bean
|
||||
public CorsConfigurationSource corsConfigurationSource() {
|
||||
CorsConfiguration configuration = new CorsConfiguration();
|
||||
configuration.setAllowedOrigins(List.of("http://localhost:3000"));
|
||||
configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
|
||||
configuration.setAllowedHeaders(List.of("*"));
|
||||
configuration.setAllowCredentials(true);
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", configuration);
|
||||
return source;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package com.bivashy.backend.composer.config;
|
||||
|
||||
import org.modelmapper.ModelMapper;
|
||||
import org.modelmapper.config.Configuration.AccessLevel;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class ModelMapperConfig {
|
||||
@Bean
|
||||
public ModelMapper modelMapper() {
|
||||
ModelMapper modelMapper = new ModelMapper();
|
||||
modelMapper.getConfiguration()
|
||||
.setFieldAccessLevel(AccessLevel.PRIVATE)
|
||||
.setFieldMatchingEnabled(true);
|
||||
return modelMapper;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
package com.bivashy.backend.composer.config;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
import com.bivashy.backend.composer.auth.CustomUserDetails;
|
||||
import com.bivashy.backend.composer.model.User;
|
||||
import com.bivashy.backend.composer.repository.UserRepository;
|
||||
import com.bivashy.backend.composer.service.CustomUserDetailsService;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http.authorizeHttpRequests(auth -> auth
|
||||
.anyRequest().authenticated())
|
||||
.httpBasic(Customizer.withDefaults())
|
||||
// TODO: Temporary to test API, remove after testing
|
||||
.csrf(c -> c.disable());
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CustomUserDetailsService customUserDetailsService(UserRepository repository) {
|
||||
CustomUserDetails defaultUser = new CustomUserDetails(1, "user", passwordEncoder().encode("password"));
|
||||
Optional<User> user = repository.findById(defaultUser.getId());
|
||||
if (user.isEmpty()) {
|
||||
repository.save(new User(defaultUser.getUsername()));
|
||||
}
|
||||
return new CustomUserDetailsService(defaultUser);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package com.bivashy.backend.composer.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.bivashy.backend.composer.auth.CustomUserDetails;
|
||||
import com.bivashy.backend.composer.dto.playlist.PlaylistCreateDTO;
|
||||
import com.bivashy.backend.composer.dto.playlist.PlaylistReadDTO;
|
||||
import com.bivashy.backend.composer.service.PlaylistService;
|
||||
|
||||
@RestController
|
||||
public class PlaylistController {
|
||||
private final PlaylistService service;
|
||||
|
||||
public PlaylistController(PlaylistService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@GetMapping("/playlists")
|
||||
public List<PlaylistReadDTO> playlists(@AuthenticationPrincipal CustomUserDetails user) {
|
||||
return service.findPlaylists(user.getId());
|
||||
}
|
||||
|
||||
@PostMapping("/playlist")
|
||||
public PlaylistReadDTO createPlaylist(@AuthenticationPrincipal CustomUserDetails user,
|
||||
@RequestBody PlaylistCreateDTO playlist) {
|
||||
return service.createPlaylist(user.getId(), playlist);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package com.bivashy.backend.composer.converter;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.bivashy.backend.composer.dto.playlist.PlaylistCreateDTO;
|
||||
import com.bivashy.backend.composer.dto.playlist.PlaylistReadDTO;
|
||||
import com.bivashy.backend.composer.model.Playlist;
|
||||
import com.bivashy.backend.composer.model.User;
|
||||
|
||||
@Component
|
||||
public class PlaylistConverter {
|
||||
public PlaylistReadDTO convertToRead(Playlist playlist) {
|
||||
return new PlaylistReadDTO(playlist.getId(), playlist.getOwner().getId(), playlist.getTitle(),
|
||||
playlist.getCreatedAt(),
|
||||
playlist.getUpdatedAt());
|
||||
}
|
||||
|
||||
public Playlist convertFromCreate(long userId, PlaylistCreateDTO playlist) {
|
||||
return new Playlist(new User(userId), playlist.title());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
package com.bivashy.backend.composer.dto.playlist;
|
||||
|
||||
public record PlaylistCreateDTO(String title) {
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package com.bivashy.backend.composer.dto.playlist;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public record PlaylistReadDTO(
|
||||
long id,
|
||||
long ownerId,
|
||||
String title,
|
||||
LocalDateTime createdAt,
|
||||
LocalDateTime updatedAt) {
|
||||
public PlaylistReadDTO withUserId(long userId) {
|
||||
return new PlaylistReadDTO(this.id, userId, this.title, this.createdAt, this.updatedAt);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,79 @@
|
||||
package com.bivashy.backend.composer.model;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.JoinTable;
|
||||
import jakarta.persistence.ManyToMany;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OrderBy;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "playlist")
|
||||
public class Playlist {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "owner_id", nullable = false)
|
||||
private User owner;
|
||||
|
||||
@Column(nullable = false, length = 500)
|
||||
private String title;
|
||||
|
||||
@Column(name = "created_at", nullable = false, updatable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Column(name = "updated_at", nullable = false)
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
@ManyToMany
|
||||
@JoinTable(name = "playlist_track", joinColumns = @JoinColumn(name = "playlist_id"), inverseJoinColumns = @JoinColumn(name = "track_id"))
|
||||
@OrderBy("order ASC")
|
||||
private Set<Track> tracks = new HashSet<>();
|
||||
|
||||
Playlist() {
|
||||
}
|
||||
|
||||
public Playlist(User owner, String title) {
|
||||
this.owner = owner;
|
||||
this.title = title;
|
||||
this.createdAt = LocalDateTime.now();
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public User getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public Set<Track> getTracks() {
|
||||
return tracks;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
package com.bivashy.backend.composer.model;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "source_provider")
|
||||
public class SourceProvider {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false, length = 500)
|
||||
private String name;
|
||||
|
||||
@OneToMany(mappedBy = "provider", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private Set<SourceType> sourceTypes = new HashSet<>();
|
||||
|
||||
SourceProvider() {
|
||||
}
|
||||
|
||||
public SourceProvider(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Set<SourceType> getSourceTypes() {
|
||||
return sourceTypes;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
package com.bivashy.backend.composer.model;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "source_type")
|
||||
public class SourceType {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "provider_id", nullable = false)
|
||||
private SourceProvider provider;
|
||||
|
||||
@Column(nullable = false, length = 500)
|
||||
private String name;
|
||||
|
||||
@OneToMany(mappedBy = "sourceType", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private Set<TrackSource> trackSources = new HashSet<>();
|
||||
|
||||
SourceType() {
|
||||
}
|
||||
|
||||
public SourceType(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public SourceProvider getProvider() {
|
||||
return provider;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Set<TrackSource> getTrackSources() {
|
||||
return trackSources;
|
||||
}
|
||||
|
||||
}
|
||||
65
src/main/java/com/bivashy/backend/composer/model/Track.java
Normal file
65
src/main/java/com/bivashy/backend/composer/model/Track.java
Normal file
@ -0,0 +1,65 @@
|
||||
package com.bivashy.backend.composer.model;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToMany;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "track")
|
||||
public class Track {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "source_id", nullable = false)
|
||||
private TrackSource source;
|
||||
|
||||
@OneToMany(mappedBy = "track", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private Set<TrackMetadata> metadata = new HashSet<>();
|
||||
|
||||
@OneToMany(mappedBy = "track", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private Set<TrackVersion> versions = new HashSet<>();
|
||||
|
||||
@ManyToMany(mappedBy = "tracks")
|
||||
private Set<Playlist> playlists = new HashSet<>();
|
||||
|
||||
Track() {
|
||||
}
|
||||
|
||||
public Track(TrackSource source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public TrackSource getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public Set<TrackMetadata> getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
public Set<TrackVersion> getVersions() {
|
||||
return versions;
|
||||
}
|
||||
|
||||
public Set<Playlist> getPlaylists() {
|
||||
return playlists;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,91 @@
|
||||
package com.bivashy.backend.composer.model;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.OneToOne;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "track_metadata")
|
||||
public class TrackMetadata {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@OneToOne
|
||||
@JoinColumn(name = "track_id", nullable = false)
|
||||
private Track track;
|
||||
|
||||
@Column(nullable = false, length = 500)
|
||||
private String title;
|
||||
|
||||
@Column(name = "audio_path", nullable = false, length = 500)
|
||||
private String audioPath;
|
||||
|
||||
@Column(length = 500)
|
||||
private String artist;
|
||||
|
||||
@Column(name = "thumbnail_path", length = 500)
|
||||
private String thumbnailPath;
|
||||
|
||||
@Column(name = "duration_seconds")
|
||||
private Integer durationSeconds;
|
||||
|
||||
@OneToMany(mappedBy = "metadata", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private Set<TrackVersion> versions = new HashSet<>();
|
||||
|
||||
TrackMetadata() {
|
||||
}
|
||||
|
||||
public TrackMetadata(Track track, String title, String audioPath, String artist, String thumbnailPath,
|
||||
Integer durationSeconds) {
|
||||
this.track = track;
|
||||
this.title = title;
|
||||
this.audioPath = audioPath;
|
||||
this.artist = artist;
|
||||
this.thumbnailPath = thumbnailPath;
|
||||
this.durationSeconds = durationSeconds;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Track getTrack() {
|
||||
return track;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getAudioPath() {
|
||||
return audioPath;
|
||||
}
|
||||
|
||||
public String getArtist() {
|
||||
return artist;
|
||||
}
|
||||
|
||||
public String getThumbnailPath() {
|
||||
return thumbnailPath;
|
||||
}
|
||||
|
||||
public Integer getDurationSeconds() {
|
||||
return durationSeconds;
|
||||
}
|
||||
|
||||
public Set<TrackVersion> getVersions() {
|
||||
return versions;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,91 @@
|
||||
package com.bivashy.backend.composer.model;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "track_source")
|
||||
public class TrackSource {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "source_url", nullable = false, length = 500)
|
||||
private String sourceUrl;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "source_type_id", nullable = false)
|
||||
private SourceType sourceType;
|
||||
|
||||
@Column(name = "last_fetched_at", nullable = false)
|
||||
private LocalDateTime lastFetchedAt;
|
||||
|
||||
@Column(name = "created_at", nullable = false, updatable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Column(name = "updated_at", nullable = false)
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
@OneToMany(mappedBy = "source", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private Set<Track> tracks = new HashSet<>();
|
||||
|
||||
@OneToMany(mappedBy = "source", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private Set<TrackVersion> trackVersions = new HashSet<>();
|
||||
|
||||
TrackSource() {
|
||||
}
|
||||
|
||||
public TrackSource(String sourceUrl, SourceType sourceType, LocalDateTime lastFetchedAt) {
|
||||
this.sourceUrl = sourceUrl;
|
||||
this.sourceType = sourceType;
|
||||
this.lastFetchedAt = lastFetchedAt;
|
||||
this.createdAt = LocalDateTime.now();
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getSourceUrl() {
|
||||
return sourceUrl;
|
||||
}
|
||||
|
||||
public SourceType getSourceType() {
|
||||
return sourceType;
|
||||
}
|
||||
|
||||
public LocalDateTime getLastFetchedAt() {
|
||||
return lastFetchedAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public Set<Track> getTracks() {
|
||||
return tracks;
|
||||
}
|
||||
|
||||
public Set<TrackVersion> getTrackVersions() {
|
||||
return trackVersions;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
package com.bivashy.backend.composer.model;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "track_version")
|
||||
public class TrackVersion {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "track_id", nullable = false)
|
||||
private Track track;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "metadata_id", nullable = false)
|
||||
private TrackMetadata metadata;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "source_id", nullable = false)
|
||||
private TrackSource source;
|
||||
|
||||
@Column(name = "created_at", nullable = false, updatable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
TrackVersion() {
|
||||
}
|
||||
|
||||
public TrackVersion(Track track, TrackMetadata metadata, TrackSource source) {
|
||||
this.track = track;
|
||||
this.metadata = metadata;
|
||||
this.source = source;
|
||||
this.createdAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Track getTrack() {
|
||||
return track;
|
||||
}
|
||||
|
||||
public TrackMetadata getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
public TrackSource getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
}
|
||||
70
src/main/java/com/bivashy/backend/composer/model/User.java
Normal file
70
src/main/java/com/bivashy/backend/composer/model/User.java
Normal file
@ -0,0 +1,70 @@
|
||||
package com.bivashy.backend.composer.model;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "users")
|
||||
public class User {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false, length = 500)
|
||||
private String name;
|
||||
|
||||
@Column(name = "created_at", nullable = false, updatable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Column(name = "updated_at", nullable = false)
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
@OneToMany(mappedBy = "owner", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private Set<Playlist> playlists = new HashSet<>();
|
||||
|
||||
User() {
|
||||
}
|
||||
|
||||
public User(Long id) {
|
||||
this.id = id;
|
||||
this.createdAt = LocalDateTime.now();
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public User(String name) {
|
||||
this.name = name;
|
||||
this.createdAt = LocalDateTime.now();
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public Set<Playlist> getPlaylists() {
|
||||
return playlists;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package com.bivashy.backend.composer.repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.bivashy.backend.composer.model.Playlist;
|
||||
|
||||
@Repository
|
||||
public interface PlaylistRepository extends JpaRepository<Playlist, Long> {
|
||||
Optional<Playlist> findById(Long id);
|
||||
|
||||
Optional<Playlist> findByOwnerId(long id);
|
||||
|
||||
List<Playlist> findAllByOwnerId(long id);
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package com.bivashy.backend.composer.repository;
|
||||
|
||||
import com.bivashy.backend.composer.model.User;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface UserRepository extends JpaRepository<User, Long> {
|
||||
Optional<User> findById(long id);
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package com.bivashy.backend.composer.service;
|
||||
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.bivashy.backend.composer.auth.CustomUserDetails;
|
||||
|
||||
@Service
|
||||
public class CustomUserDetailsService implements UserDetailsService {
|
||||
private final CustomUserDetails defaultUser;
|
||||
|
||||
public CustomUserDetailsService(CustomUserDetails defaultUser) {
|
||||
this.defaultUser = defaultUser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
return defaultUser;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package com.bivashy.backend.composer.service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.bivashy.backend.composer.converter.PlaylistConverter;
|
||||
import com.bivashy.backend.composer.dto.playlist.PlaylistCreateDTO;
|
||||
import com.bivashy.backend.composer.dto.playlist.PlaylistReadDTO;
|
||||
import com.bivashy.backend.composer.model.Playlist;
|
||||
import com.bivashy.backend.composer.repository.PlaylistRepository;
|
||||
|
||||
@Service
|
||||
public class PlaylistService {
|
||||
private final PlaylistRepository repository;
|
||||
private final PlaylistConverter converter;
|
||||
|
||||
public PlaylistService(PlaylistRepository repository, PlaylistConverter converter) {
|
||||
this.repository = repository;
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
public PlaylistReadDTO createPlaylist(long userId, PlaylistCreateDTO playlist) {
|
||||
Playlist result = repository.save(converter.convertFromCreate(userId, playlist));
|
||||
return converter.convertToRead(result);
|
||||
}
|
||||
|
||||
public List<PlaylistReadDTO> findPlaylists(long userId) {
|
||||
return repository.findAllByOwnerId(userId)
|
||||
.stream()
|
||||
.map(converter::convertToRead)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
14
src/main/resources/application.yaml
Normal file
14
src/main/resources/application.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
spring:
|
||||
application:
|
||||
name: composer
|
||||
datasource:
|
||||
driver-class-name: org.postgresql.Driver
|
||||
password: password
|
||||
url: jdbc:postgresql://postgres:5432/db
|
||||
username: user
|
||||
|
||||
logging:
|
||||
level:
|
||||
org:
|
||||
springframework:
|
||||
security: DEBUG
|
||||
@ -0,0 +1,85 @@
|
||||
CREATE TABLE IF NOT EXISTS "users" (
|
||||
"id" bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
"name" varchar(500) NOT NULL,
|
||||
"created_at" timestamp NOT NULL DEFAULT NOW(),
|
||||
"updated_at" timestamp NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "source_provider" (
|
||||
"id" bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
"name" varchar(500) NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "source_type" (
|
||||
"id" bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
"provider_id" bigint NOT NULL,
|
||||
"name" varchar(500) NOT NULL,
|
||||
CONSTRAINT "fk_source_type_provider_id"
|
||||
FOREIGN KEY ("provider_id") REFERENCES "source_provider" ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "track_source" (
|
||||
"id" bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
"source_url" varchar(500) NOT NULL,
|
||||
"source_type_id" bigint NOT NULL,
|
||||
"last_fetched_at" timestamp NOT NULL DEFAULT NOW(),
|
||||
"created_at" timestamp NOT NULL DEFAULT NOW(),
|
||||
"updated_at" timestamp NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT "fk_track_source_source_type_id"
|
||||
FOREIGN KEY ("source_type_id") REFERENCES "source_type" ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "track" (
|
||||
"id" bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
"source_id" bigint NOT NULL,
|
||||
CONSTRAINT "fk_track_source_id"
|
||||
FOREIGN KEY ("source_id") REFERENCES "track_source" ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "track_metadata" (
|
||||
"id" bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
"track_id" bigint NOT NULL,
|
||||
"title" varchar(500) NOT NULL,
|
||||
"audio_path" varchar(500) NOT NULL,
|
||||
"artist" varchar(500),
|
||||
"thumbnail_path" varchar(500),
|
||||
"duration_seconds" integer,
|
||||
CONSTRAINT "fk_track_metadata_track_id"
|
||||
FOREIGN KEY ("track_id") REFERENCES "track" ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "playlist" (
|
||||
"id" bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
"owner_id" bigint NOT NULL,
|
||||
"title" varchar(500) NOT NULL,
|
||||
"created_at" timestamp NOT NULL DEFAULT NOW(),
|
||||
"updated_at" timestamp NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT "fk_playlist_owner_id"
|
||||
FOREIGN KEY ("owner_id") REFERENCES "users" ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "playlist_track" (
|
||||
"playlist_id" bigint NOT NULL,
|
||||
"track_id" bigint NOT NULL,
|
||||
"order" bigint NOT NULL,
|
||||
CONSTRAINT "pk_playlist_track" PRIMARY KEY ("playlist_id", "track_id"),
|
||||
CONSTRAINT "fk_playlist_track_playlist_id"
|
||||
FOREIGN KEY ("playlist_id") REFERENCES "playlist" ("id"),
|
||||
CONSTRAINT "fk_playlist_track_track_id"
|
||||
FOREIGN KEY ("track_id") REFERENCES "track" ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "track_version" (
|
||||
"id" bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
"track_id" bigint NOT NULL,
|
||||
"metadata_id" bigint NOT NULL,
|
||||
"source_id" bigint NOT NULL,
|
||||
"created_at" timestamp NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT "fk_track_version_track_id"
|
||||
FOREIGN KEY ("track_id") REFERENCES "track" ("id"),
|
||||
CONSTRAINT "fk_track_version_metadata_id"
|
||||
FOREIGN KEY ("metadata_id") REFERENCES "track_metadata" ("id"),
|
||||
CONSTRAINT "fk_track_version_source_id"
|
||||
FOREIGN KEY ("source_id") REFERENCES "track_source" ("id")
|
||||
);
|
||||
|
||||
17
src/main/resources/db/migration/V1_20__insert_enums.sql
Normal file
17
src/main/resources/db/migration/V1_20__insert_enums.sql
Normal file
@ -0,0 +1,17 @@
|
||||
INSERT INTO "source_provider" ("id", "name")
|
||||
OVERRIDING SYSTEM VALUE
|
||||
VALUES
|
||||
(1, 'YOUTUBE'),
|
||||
(2, 'LOCAL'),
|
||||
(3, 'EXTERNAL')
|
||||
ON CONFLICT ("id") DO NOTHING;
|
||||
|
||||
INSERT INTO "source_type" ("id", "provider_id", "name")
|
||||
OVERRIDING SYSTEM VALUE
|
||||
VALUES
|
||||
(1, 1, 'VIDEO'),
|
||||
(2, 1, 'PLAYLIST'),
|
||||
(3, 2, 'FILE'),
|
||||
(4, 3, 'URL')
|
||||
ON CONFLICT ("id") DO NOTHING;
|
||||
|
||||
5
src/main/resources/flyway.conf
Normal file
5
src/main/resources/flyway.conf
Normal file
@ -0,0 +1,5 @@
|
||||
flyway.user=user
|
||||
flyway.password=password
|
||||
flyway.schemas=public
|
||||
flyway.url=jdbc:postgresql://postgres:5432/db
|
||||
flyway.locations=filesystem:db/migration
|
||||
@ -0,0 +1,13 @@
|
||||
package com.bivashy.backend.composer;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class ComposerApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user