diff --git a/.github/workflows/azure-webapps-java-jar.yml b/.github/workflows/azure-webapps-java-jar.yml deleted file mode 100644 index 9fe46c4..0000000 --- a/.github/workflows/azure-webapps-java-jar.yml +++ /dev/null @@ -1,79 +0,0 @@ -# This workflow will build and push a Java application to an Azure Web App when a commit is pushed to your default branch. -# -# This workflow assumes you have already created the target Azure App Service web app. -# For instructions see https://docs.microsoft.com/en-us/azure/app-service/quickstart-java?tabs=javase&pivots=platform-linux -# -# To configure this workflow: -# -# 1. Download the Publish Profile for your Azure Web App. You can download this file from the Overview page of your Web App in the Azure Portal. -# For more information: https://docs.microsoft.com/en-us/azure/app-service/deploy-github-actions?tabs=applevel#generate-deployment-credentials -# -# 2. Create a secret in your repository named AZURE_WEBAPP_PUBLISH_PROFILE, paste the publish profile contents as the value of the secret. -# For instructions on obtaining the publish profile see: https://docs.microsoft.com/azure/app-service/deploy-github-actions#configure-the-github-secret -# -# 3. Change the value for the AZURE_WEBAPP_NAME. Optionally, change the JAVA_VERSION environment variable below. -# -# For more information on GitHub Actions for Azure: https://github.com/Azure/Actions -# For more information on the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy -# For more samples to get started with GitHub Action workflows to deploy to Azure: https://github.com/Azure/actions-workflow-samples - -name: Build and deploy JAR app to Azure Web App - -env: - AZURE_WEBAPP_NAME: spotify-profile # set this to the name of your Azure Web App - JAVA_VERSION: '17' # set this to the Java version to use - DISTRIBUTION: zulu # set this to the Java distribution - -on: - push: - branches: [ "main" ] - workflow_dispatch: - -permissions: - contents: read - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: Set up Java version - uses: actions/setup-java@v3.0.0 - with: - java-version: ${{ env.JAVA_VERSION }} - distribution: ${{ env.DISTRIBUTION }} - cache: 'maven' - - - name: Build with Maven - run: mvn clean install - - - name: Upload artifact for deployment job - uses: actions/upload-artifact@v3 - with: - name: java-app - path: '${{ github.workspace }}/target/*.jar' - - deploy: - permissions: - contents: none - runs-on: ubuntu-latest - needs: build - environment: - name: 'Development' - url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} - - steps: - - name: Download artifact from build job - uses: actions/download-artifact@v3 - with: - name: java-app - - - name: Deploy to Azure Web App - id: deploy-to-webapp - uses: azure/webapps-deploy@v2 - with: - app-name: ${{ env.AZURE_WEBAPP_NAME }} - publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }} - package: '*.jar' diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..a55e7a1 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/api/.dockerignore b/api/.dockerignore new file mode 100644 index 0000000..48aa3df --- /dev/null +++ b/api/.dockerignore @@ -0,0 +1,2 @@ +target/ +*.log \ No newline at end of file diff --git a/api/.gitignore b/api/.gitignore index afd694c..8b3f220 100644 --- a/api/.gitignore +++ b/api/.gitignore @@ -32,4 +32,5 @@ build/ ### VS Code ### .vscode/ -.env +application-dev.yml +application-prod.yml diff --git a/api/Dockerfile b/api/Dockerfile new file mode 100644 index 0000000..8118a7f --- /dev/null +++ b/api/Dockerfile @@ -0,0 +1,30 @@ +# Use Maven image for the build stage +FROM maven:3.8.1-openjdk-17-slim as build + +# Set the current working directory inside the image +WORKDIR /api + +# Copy pom.xml file to the /api directory +COPY pom.xml . + +# Download all required dependencies into one layer +RUN mvn -B dependency:resolve dependency:resolve-plugins + +# Copy source code to the /api directory +COPY src ./src + +# package the application +RUN mvn package -DskipTests + +# Start a new stage for running the application +# Starting a new stage for running the application is a part of the multi-stage build process in Docker. The main advantage of this approach is that it allows you to separate the build stage and the run stage, which can significantly reduce the size of the final Docker image. In the build stage, you typically install all the necessary build tools and dependencies, and compile your application. This stage often results in a large Docker image because of all the build tools and dependencies. In the run stage, you start from a new, often smaller base image, and copy only the compiled application (and any runtime dependencies) from the build stage. This results in a smaller final Docker image because it doesn't include the build tools and dependencies that are not needed to run the application. +FROM openjdk:17-slim + +# Set the current working directory inside the image +WORKDIR /app + +# Copy the jar file from the build stage +COPY --from=build /api/target/*.jar app.jar + +# Run the application +CMD ["java", "-jar", "app.jar"] diff --git a/api/pom.xml b/api/pom.xml index 5e12c20..1deeac8 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -1,59 +1,51 @@ - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.2.3 - - - com - wissem - 0.0.1-SNAPSHOT - wissem - spotify profile api - - 17 - - - - org.springframework.boot - spring-boot-starter-web - - - - org.projectlombok - lombok - true - - - me.paulschwarz - spring-dotenv - 4.0.0 - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - org.projectlombok - lombok - - - - - - - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.2.3 + + + com + wissem + 0.0.1-SNAPSHOT + wissem + spotify profile api + + 17 + + + + org.springframework.boot + spring-boot-starter-web + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + diff --git a/api/src/main/java/com/wissem/Controller.java b/api/src/main/java/com/wissem/Controller.java index 80cb6f1..9c8ccc0 100644 --- a/api/src/main/java/com/wissem/Controller.java +++ b/api/src/main/java/com/wissem/Controller.java @@ -6,24 +6,30 @@ import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.RestTemplate; +import org.springframework.web.servlet.view.RedirectView; import org.springframework.web.util.UriComponentsBuilder; import java.security.NoSuchAlgorithmException; import java.util.Map; @RestController -@CrossOrigin(origins = "http://localhost:4200") public class Controller { @Value("${spotify.client_id}") String CLIENT_ID; @Value("${spotify.client_secret}") String CLIENT_SECRET; @Value("${spotify.code_verifier}") String CODE_VERIFIER; - - String redirectUri = "http://localhost:4200/login"; - String scope = "user-read-private user-read-email user-read-recently-played user-top-read user-follow-read user-follow-modify playlist-read-private playlist-read-collaborative playlist-modify-public"; + @Value("${spotify.redirect_uri}") String redirectUri; + String scope = + "user-read-private user-read-email user-read-recently-played user-top-read " + + "user-follow-read user-follow-modify playlist-read-private " + + "playlist-read-collaborative playlist-modify-public"; String authUrl = "https://accounts.spotify.com/authorize"; String uri = "https://accounts.spotify.com/api/token"; - + + @GetMapping("/") + public RedirectView index() { + return new RedirectView("index.html"); + } @GetMapping("/api/v1/auth-url") public ResponseEntity> buildAuthUrl() @@ -38,8 +44,7 @@ public ResponseEntity> buildAuthUrl() .queryParam("code_challenge", codeChallenge) .queryParam("redirect_uri", redirectUri) .toUriString(); - return new ResponseEntity<>( - Map.of("url", authUrlWithParams), HttpStatus.OK); + return new ResponseEntity<>(Map.of("url", authUrlWithParams), HttpStatus.OK); } @GetMapping("/api/v1/token") @@ -49,7 +54,7 @@ public ResponseEntity getToken(@RequestParam String code) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); // setting the body - MultiValueMap body= new LinkedMultiValueMap(); + MultiValueMap body = new LinkedMultiValueMap(); body.add("client_id", CLIENT_ID); body.add("grant_type", "authorization_code"); body.add("code", code); @@ -59,7 +64,8 @@ public ResponseEntity getToken(@RequestParam String code) { } @PostMapping("/api/v1/refresh/token") - public ResponseEntity getRefreshToken(@RequestBody RefreshTokenBody request) { + public ResponseEntity getRefreshToken( + @RequestBody RefreshTokenBody request) { RestTemplate restTemplate = new RestTemplate(); // setting the headers HttpHeaders headers = new HttpHeaders(); @@ -72,17 +78,21 @@ public ResponseEntity getRefreshToken(@RequestBody RefreshTokenBo return getTokenResponseResponseEntity(restTemplate, headers, body); } - private ResponseEntity getTokenResponseResponseEntity(RestTemplate restTemplate, HttpHeaders headers, MultiValueMap body) { - HttpEntity> - entity =new HttpEntity>(body, headers); - ResponseEntity response = restTemplate.postForEntity(uri, entity , TokenResponse.class); - return new ResponseEntity(response.getBody(), response.getStatusCode()); + private ResponseEntity getTokenResponseResponseEntity( + RestTemplate restTemplate, HttpHeaders headers, MultiValueMap body) { + HttpEntity> entity = + new HttpEntity>(body, headers); + ResponseEntity response = + restTemplate.postForEntity(uri, entity, TokenResponse.class); + return new ResponseEntity(response.getBody(), + response.getStatusCode()); } } -record TokenResponse(String access_token, String token_type, int expires_in, String refresh_token, String scope) { +record TokenResponse(String access_token, String token_type, int expires_in, + String refresh_token, String scope) { } record RefreshTokenBody(String refreshToken) { diff --git a/api/src/main/java/com/wissem/WebConfig.java b/api/src/main/java/com/wissem/WebConfig.java new file mode 100644 index 0000000..66d0068 --- /dev/null +++ b/api/src/main/java/com/wissem/WebConfig.java @@ -0,0 +1,23 @@ +package com.wissem; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + @Value("${cors.allowed-origins}") private String allowedOrigins; + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry + .addMapping("/**") + .allowedOrigins(allowedOrigins) + .allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS") + .allowedHeaders("*") + .allowCredentials(true); + } + +} diff --git a/api/src/main/resources/.env.example b/api/src/main/resources/.env.example deleted file mode 100644 index 08cf726..0000000 --- a/api/src/main/resources/.env.example +++ /dev/null @@ -1,3 +0,0 @@ -CLIENT_ID= -CLIENT_SECRET= -CODE_VERIFIER= \ No newline at end of file diff --git a/api/src/main/resources/application.yml b/api/src/main/resources/application.yml index a5ee6de..eb4a1bd 100644 --- a/api/src/main/resources/application.yml +++ b/api/src/main/resources/application.yml @@ -1,7 +1,6 @@ server: port: 5000 -spotify: - client_id: ${CLIENT_ID} - client_secret: ${CLIENT_SECRET} - code_verifier: ${CODE_VERIFIER} \ No newline at end of file +spring: + profiles: + active: dev \ No newline at end of file diff --git a/api/src/main/resources/static/index.html b/api/src/main/resources/static/index.html new file mode 100644 index 0000000..2404d00 --- /dev/null +++ b/api/src/main/resources/static/index.html @@ -0,0 +1,10 @@ + + + + + Spotify Profile API + + +

Welcome to Spotify Profile API

+ + diff --git a/client/.dockerignore b/client/.dockerignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/client/.dockerignore @@ -0,0 +1 @@ +node_modules diff --git a/client/Dockerfile b/client/Dockerfile new file mode 100644 index 0000000..5c53e01 --- /dev/null +++ b/client/Dockerfile @@ -0,0 +1,10 @@ +FROM node:20-alpine as build + +WORKDIR /client + +COPY package.json ./ + +COPY yarn.lock ./ + +RUN yarn install + diff --git a/client/angular.json b/client/angular.json index 08984e0..2086e12 100644 --- a/client/angular.json +++ b/client/angular.json @@ -51,7 +51,13 @@ "vendorChunk": true, "extractLicenses": false, "sourceMap": true, - "namedChunks": true + "namedChunks": true, + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.development.ts" + } + ] } }, "defaultConfiguration": "production" diff --git a/client/src/app/components/navigation/navbar/navbar.component.html b/client/src/app/components/navigation/navbar/navbar.component.html index 0eab6b3..5985f7f 100644 --- a/client/src/app/components/navigation/navbar/navbar.component.html +++ b/client/src/app/components/navigation/navbar/navbar.component.html @@ -1,40 +1,42 @@ diff --git a/client/src/app/components/track/track.component.css b/client/src/app/components/track/track.component.css index 13b8ec3..1fdbfec 100644 --- a/client/src/app/components/track/track.component.css +++ b/client/src/app/components/track/track.component.css @@ -8,7 +8,7 @@ width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); - inset: 0px; + inset: 0; color: rgb(255, 255, 255); opacity: 0; transition: all 0.25s cubic-bezier(0.3, 0, 0.4, 1) 0s; @@ -16,4 +16,4 @@ .img-container:hover .overlay { opacity: 1; -} \ No newline at end of file +} diff --git a/client/src/app/components/track/track.component.html b/client/src/app/components/track/track.component.html index bec9e3f..8b7cbf5 100644 --- a/client/src/app/components/track/track.component.html +++ b/client/src/app/components/track/track.component.html @@ -1,5 +1,5 @@ -
+
track picture
info diff --git a/client/src/app/interceptors/token.interceptor.ts b/client/src/app/interceptors/token.interceptor.ts index b91bc0c..6ecd792 100644 --- a/client/src/app/interceptors/token.interceptor.ts +++ b/client/src/app/interceptors/token.interceptor.ts @@ -2,11 +2,12 @@ import { Injectable } from '@angular/core'; import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; import { BehaviorSubject, catchError, filter, Observable, switchMap, take, throwError } from "rxjs"; import { AuthService } from "../services/auth.service"; +import { environment } from "../../environments/environment"; @Injectable() export class TokenInterceptor implements HttpInterceptor { private baseURL: string = 'https://api.spotify.com/v1'; - private apiURL: string = 'http://localhost:5000/api/v1'; + private apiURL: string = environment.apiURL; private isRefreshing = false; private refreshTokenSubject: BehaviorSubject = new BehaviorSubject(null); diff --git a/client/src/environments/environment.development.ts b/client/src/environments/environment.development.ts new file mode 100644 index 0000000..462cc2b --- /dev/null +++ b/client/src/environments/environment.development.ts @@ -0,0 +1,4 @@ +export const environment = { + production: true, + apiURL: 'http://localhost:5000/api/v1' +}; diff --git a/client/src/index.html b/client/src/index.html index 2eca958..967ef01 100644 --- a/client/src/index.html +++ b/client/src/index.html @@ -7,6 +7,7 @@ +