├── .gitattributes ├── part-2 ├── src │ ├── main │ │ ├── resources │ │ │ ├── application.properties │ │ │ ├── static │ │ │ │ └── css │ │ │ │ │ └── styles.css │ │ │ └── templates │ │ │ │ ├── about.html │ │ │ │ └── index.html │ │ └── java │ │ │ └── com │ │ │ └── sivalabs │ │ │ └── urlshortener │ │ │ ├── SpringBootUrlShortenerApplication.java │ │ │ └── HomeController.java │ └── test │ │ └── java │ │ └── com │ │ └── sivalabs │ │ └── urlshortener │ │ └── SpringBootUrlShortenerApplicationTests.java ├── README.md ├── .gitignore └── .mvn │ └── wrapper │ └── maven-wrapper.properties ├── part-1 ├── home.png ├── login.png ├── my-urls.png ├── register.png ├── admin-dashboard.png └── README.md ├── springboot-url-shortener.png ├── part-11 ├── README.md ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── sivalabs │ │ │ │ └── urlshortener │ │ │ │ ├── domain │ │ │ │ ├── models │ │ │ │ │ ├── Role.java │ │ │ │ │ ├── CreateUserCmd.java │ │ │ │ │ ├── CreateShortUrlCmd.java │ │ │ │ │ ├── UserDto.java │ │ │ │ │ ├── ShortUrlDto.java │ │ │ │ │ └── PagedResult.java │ │ │ │ ├── exceptions │ │ │ │ │ └── ShortUrlNotFoundException.java │ │ │ │ ├── repositories │ │ │ │ │ └── ShortUrlRepository.java │ │ │ │ └── services │ │ │ │ │ ├── EntityMapper.java │ │ │ │ │ ├── UrlExistenceValidator.java │ │ │ │ │ ├── UserService.java │ │ │ │ │ └── SecurityUserDetailsService.java │ │ │ │ ├── web │ │ │ │ ├── dtos │ │ │ │ │ ├── CreateShortUrlForm.java │ │ │ │ │ └── RegisterUserRequest.java │ │ │ │ ├── GlobalExceptionHandler.java │ │ │ │ └── controllers │ │ │ │ │ └── SecurityUtils.java │ │ │ │ ├── SpringBootUrlShortenerApplication.java │ │ │ │ └── ApplicationProperties.java │ │ └── resources │ │ │ ├── db │ │ │ └── migration │ │ │ │ ├── V3__update_user_passwords.sql │ │ │ │ └── V1__create_tables.sql │ │ │ ├── static │ │ │ └── css │ │ │ │ └── styles.css │ │ │ ├── templates │ │ │ └── error │ │ │ │ ├── 404.html │ │ │ │ └── 500.html │ │ │ └── application.properties │ └── test │ │ └── java │ │ └── com │ │ └── sivalabs │ │ └── urlshortener │ │ ├── SpringBootUrlShortenerApplicationTests.java │ │ └── PasswordUtility.java ├── compose.yaml ├── .gitignore └── .mvn │ └── wrapper │ └── maven-wrapper.properties ├── part-10 ├── README.md ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── sivalabs │ │ │ │ └── urlshortener │ │ │ │ ├── domain │ │ │ │ ├── models │ │ │ │ │ ├── Role.java │ │ │ │ │ ├── CreateShortUrlCmd.java │ │ │ │ │ ├── UserDto.java │ │ │ │ │ ├── ShortUrlDto.java │ │ │ │ │ └── PagedResult.java │ │ │ │ ├── exceptions │ │ │ │ │ └── ShortUrlNotFoundException.java │ │ │ │ ├── repositories │ │ │ │ │ ├── UserRepository.java │ │ │ │ │ └── ShortUrlRepository.java │ │ │ │ └── services │ │ │ │ │ ├── EntityMapper.java │ │ │ │ │ ├── UrlExistenceValidator.java │ │ │ │ │ └── SecurityUserDetailsService.java │ │ │ │ ├── web │ │ │ │ ├── dtos │ │ │ │ │ └── CreateShortUrlForm.java │ │ │ │ ├── GlobalExceptionHandler.java │ │ │ │ └── controllers │ │ │ │ │ └── SecurityUtils.java │ │ │ │ ├── SpringBootUrlShortenerApplication.java │ │ │ │ └── ApplicationProperties.java │ │ └── resources │ │ │ ├── db │ │ │ └── migration │ │ │ │ ├── V3__update_user_passwords.sql │ │ │ │ └── V1__create_tables.sql │ │ │ ├── static │ │ │ └── css │ │ │ │ └── styles.css │ │ │ ├── templates │ │ │ └── error │ │ │ │ ├── 404.html │ │ │ │ └── 500.html │ │ │ └── application.properties │ └── test │ │ └── java │ │ └── com │ │ └── sivalabs │ │ └── urlshortener │ │ ├── SpringBootUrlShortenerApplicationTests.java │ │ └── PasswordUtility.java ├── compose.yaml ├── .gitignore └── .mvn │ └── wrapper │ └── maven-wrapper.properties ├── part-6 ├── README.md ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── sivalabs │ │ │ │ └── urlshortener │ │ │ │ ├── domain │ │ │ │ ├── models │ │ │ │ │ ├── Role.java │ │ │ │ │ ├── CreateShortUrlCmd.java │ │ │ │ │ ├── UserDto.java │ │ │ │ │ └── ShortUrlDto.java │ │ │ │ ├── exceptions │ │ │ │ │ └── ShortUrlNotFoundException.java │ │ │ │ ├── repositories │ │ │ │ │ └── ShortUrlRepository.java │ │ │ │ └── services │ │ │ │ │ ├── EntityMapper.java │ │ │ │ │ └── UrlExistenceValidator.java │ │ │ │ ├── web │ │ │ │ ├── dtos │ │ │ │ │ └── CreateShortUrlForm.java │ │ │ │ └── GlobalExceptionHandler.java │ │ │ │ ├── SpringBootUrlShortenerApplication.java │ │ │ │ └── ApplicationProperties.java │ │ └── resources │ │ │ ├── static │ │ │ └── css │ │ │ │ └── styles.css │ │ │ ├── templates │ │ │ └── error │ │ │ │ ├── 404.html │ │ │ │ └── 500.html │ │ │ ├── application.properties │ │ │ └── db │ │ │ └── migration │ │ │ └── V1__create_tables.sql │ └── test │ │ └── java │ │ └── com │ │ └── sivalabs │ │ └── urlshortener │ │ └── SpringBootUrlShortenerApplicationTests.java ├── compose.yaml ├── .gitignore └── .mvn │ └── wrapper │ └── maven-wrapper.properties ├── part-12 ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── sivalabs │ │ │ │ └── urlshortener │ │ │ │ ├── domain │ │ │ │ ├── models │ │ │ │ │ ├── Role.java │ │ │ │ │ ├── CreateUserCmd.java │ │ │ │ │ ├── CreateShortUrlCmd.java │ │ │ │ │ ├── UserDto.java │ │ │ │ │ ├── ShortUrlDto.java │ │ │ │ │ └── PagedResult.java │ │ │ │ ├── exceptions │ │ │ │ │ └── ShortUrlNotFoundException.java │ │ │ │ ├── repositories │ │ │ │ │ └── ShortUrlRepository.java │ │ │ │ └── services │ │ │ │ │ ├── EntityMapper.java │ │ │ │ │ ├── UrlExistenceValidator.java │ │ │ │ │ ├── UserService.java │ │ │ │ │ └── SecurityUserDetailsService.java │ │ │ │ ├── web │ │ │ │ ├── dtos │ │ │ │ │ ├── CreateShortUrlForm.java │ │ │ │ │ └── RegisterUserRequest.java │ │ │ │ ├── GlobalExceptionHandler.java │ │ │ │ └── controllers │ │ │ │ │ └── SecurityUtils.java │ │ │ │ ├── SpringBootUrlShortenerApplication.java │ │ │ │ └── ApplicationProperties.java │ │ └── resources │ │ │ ├── db │ │ │ └── migration │ │ │ │ ├── V3__update_user_passwords.sql │ │ │ │ └── V1__create_tables.sql │ │ │ ├── static │ │ │ └── css │ │ │ │ └── styles.css │ │ │ ├── templates │ │ │ └── error │ │ │ │ ├── 404.html │ │ │ │ └── 500.html │ │ │ └── application.properties │ └── test │ │ └── java │ │ └── com │ │ └── sivalabs │ │ └── urlshortener │ │ ├── TestSpringBootUrlShortenerApplication.java │ │ ├── SpringBootUrlShortenerApplicationTests.java │ │ ├── PasswordUtility.java │ │ └── TestcontainersConfiguration.java ├── compose.yaml ├── README.md ├── .gitignore ├── docker │ └── compose.yaml └── .mvn │ └── wrapper │ └── maven-wrapper.properties ├── part-3 ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── sivalabs │ │ │ │ └── urlshortener │ │ │ │ ├── domain │ │ │ │ ├── models │ │ │ │ │ └── Role.java │ │ │ │ ├── repositories │ │ │ │ │ └── ShortUrlRepository.java │ │ │ │ └── services │ │ │ │ │ └── ShortUrlService.java │ │ │ │ ├── SpringBootUrlShortenerApplication.java │ │ │ │ └── web │ │ │ │ └── controllers │ │ │ │ └── HomeController.java │ │ └── resources │ │ │ ├── application.properties │ │ │ ├── static │ │ │ └── css │ │ │ │ └── styles.css │ │ │ └── db │ │ │ └── migration │ │ │ └── V1__create_tables.sql │ └── test │ │ └── java │ │ └── com │ │ └── sivalabs │ │ └── urlshortener │ │ └── SpringBootUrlShortenerApplicationTests.java ├── compose.yaml ├── README.md ├── .gitignore └── .mvn │ └── wrapper │ └── maven-wrapper.properties ├── part-4 ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── sivalabs │ │ │ │ └── urlshortener │ │ │ │ ├── domain │ │ │ │ ├── models │ │ │ │ │ ├── Role.java │ │ │ │ │ ├── UserDto.java │ │ │ │ │ └── ShortUrlDto.java │ │ │ │ ├── repositories │ │ │ │ │ └── ShortUrlRepository.java │ │ │ │ └── services │ │ │ │ │ ├── ShortUrlService.java │ │ │ │ │ └── EntityMapper.java │ │ │ │ ├── SpringBootUrlShortenerApplication.java │ │ │ │ └── web │ │ │ │ └── controllers │ │ │ │ └── HomeController.java │ │ └── resources │ │ │ ├── application.properties │ │ │ ├── static │ │ │ └── css │ │ │ │ └── styles.css │ │ │ └── db │ │ │ └── migration │ │ │ └── V1__create_tables.sql │ └── test │ │ └── java │ │ └── com │ │ └── sivalabs │ │ └── urlshortener │ │ └── SpringBootUrlShortenerApplicationTests.java ├── README.md ├── compose.yaml ├── .gitignore └── .mvn │ └── wrapper │ └── maven-wrapper.properties ├── part-5 ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── sivalabs │ │ │ │ └── urlshortener │ │ │ │ ├── domain │ │ │ │ ├── models │ │ │ │ │ ├── Role.java │ │ │ │ │ ├── CreateShortUrlCmd.java │ │ │ │ │ ├── UserDto.java │ │ │ │ │ └── ShortUrlDto.java │ │ │ │ ├── repositories │ │ │ │ │ └── ShortUrlRepository.java │ │ │ │ └── services │ │ │ │ │ ├── EntityMapper.java │ │ │ │ │ └── UrlExistenceValidator.java │ │ │ │ ├── web │ │ │ │ └── dtos │ │ │ │ │ └── CreateShortUrlForm.java │ │ │ │ ├── SpringBootUrlShortenerApplication.java │ │ │ │ └── ApplicationProperties.java │ │ └── resources │ │ │ ├── static │ │ │ └── css │ │ │ │ └── styles.css │ │ │ ├── application.properties │ │ │ └── db │ │ │ └── migration │ │ │ └── V1__create_tables.sql │ └── test │ │ └── java │ │ └── com │ │ └── sivalabs │ │ └── urlshortener │ │ └── SpringBootUrlShortenerApplicationTests.java ├── README.md ├── compose.yaml ├── .gitignore └── .mvn │ └── wrapper │ └── maven-wrapper.properties ├── part-7 ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── sivalabs │ │ │ │ └── urlshortener │ │ │ │ ├── domain │ │ │ │ ├── models │ │ │ │ │ ├── Role.java │ │ │ │ │ ├── CreateShortUrlCmd.java │ │ │ │ │ ├── UserDto.java │ │ │ │ │ └── ShortUrlDto.java │ │ │ │ ├── exceptions │ │ │ │ │ └── ShortUrlNotFoundException.java │ │ │ │ ├── repositories │ │ │ │ │ ├── UserRepository.java │ │ │ │ │ └── ShortUrlRepository.java │ │ │ │ └── services │ │ │ │ │ ├── EntityMapper.java │ │ │ │ │ ├── UrlExistenceValidator.java │ │ │ │ │ └── SecurityUserDetailsService.java │ │ │ │ ├── web │ │ │ │ ├── dtos │ │ │ │ │ └── CreateShortUrlForm.java │ │ │ │ ├── GlobalExceptionHandler.java │ │ │ │ └── controllers │ │ │ │ │ └── SecurityUtils.java │ │ │ │ ├── SpringBootUrlShortenerApplication.java │ │ │ │ └── ApplicationProperties.java │ │ └── resources │ │ │ ├── db │ │ │ └── migration │ │ │ │ ├── V3__update_user_passwords.sql │ │ │ │ └── V1__create_tables.sql │ │ │ ├── static │ │ │ └── css │ │ │ │ └── styles.css │ │ │ ├── templates │ │ │ └── error │ │ │ │ ├── 404.html │ │ │ │ └── 500.html │ │ │ └── application.properties │ └── test │ │ └── java │ │ └── com │ │ └── sivalabs │ │ └── urlshortener │ │ ├── SpringBootUrlShortenerApplicationTests.java │ │ └── PasswordUtility.java ├── compose.yaml ├── README.md ├── .gitignore └── .mvn │ └── wrapper │ └── maven-wrapper.properties ├── part-8 ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── sivalabs │ │ │ │ └── urlshortener │ │ │ │ ├── domain │ │ │ │ ├── models │ │ │ │ │ ├── Role.java │ │ │ │ │ ├── CreateShortUrlCmd.java │ │ │ │ │ ├── UserDto.java │ │ │ │ │ └── ShortUrlDto.java │ │ │ │ ├── exceptions │ │ │ │ │ └── ShortUrlNotFoundException.java │ │ │ │ ├── repositories │ │ │ │ │ ├── UserRepository.java │ │ │ │ │ └── ShortUrlRepository.java │ │ │ │ └── services │ │ │ │ │ ├── EntityMapper.java │ │ │ │ │ ├── UrlExistenceValidator.java │ │ │ │ │ └── SecurityUserDetailsService.java │ │ │ │ ├── web │ │ │ │ ├── dtos │ │ │ │ │ └── CreateShortUrlForm.java │ │ │ │ ├── GlobalExceptionHandler.java │ │ │ │ └── controllers │ │ │ │ │ └── SecurityUtils.java │ │ │ │ ├── SpringBootUrlShortenerApplication.java │ │ │ │ └── ApplicationProperties.java │ │ └── resources │ │ │ ├── db │ │ │ └── migration │ │ │ │ ├── V3__update_user_passwords.sql │ │ │ │ └── V1__create_tables.sql │ │ │ ├── static │ │ │ └── css │ │ │ │ └── styles.css │ │ │ ├── templates │ │ │ └── error │ │ │ │ ├── 404.html │ │ │ │ └── 500.html │ │ │ └── application.properties │ └── test │ │ └── java │ │ └── com │ │ └── sivalabs │ │ └── urlshortener │ │ ├── SpringBootUrlShortenerApplicationTests.java │ │ └── PasswordUtility.java ├── README.md ├── compose.yaml ├── .gitignore └── .mvn │ └── wrapper │ └── maven-wrapper.properties ├── part-9 ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── sivalabs │ │ │ │ └── urlshortener │ │ │ │ ├── domain │ │ │ │ ├── models │ │ │ │ │ ├── Role.java │ │ │ │ │ ├── CreateShortUrlCmd.java │ │ │ │ │ ├── UserDto.java │ │ │ │ │ ├── ShortUrlDto.java │ │ │ │ │ └── PagedResult.java │ │ │ │ ├── exceptions │ │ │ │ │ └── ShortUrlNotFoundException.java │ │ │ │ ├── repositories │ │ │ │ │ ├── UserRepository.java │ │ │ │ │ └── ShortUrlRepository.java │ │ │ │ └── services │ │ │ │ │ ├── EntityMapper.java │ │ │ │ │ ├── UrlExistenceValidator.java │ │ │ │ │ └── SecurityUserDetailsService.java │ │ │ │ ├── web │ │ │ │ ├── dtos │ │ │ │ │ └── CreateShortUrlForm.java │ │ │ │ ├── GlobalExceptionHandler.java │ │ │ │ └── controllers │ │ │ │ │ └── SecurityUtils.java │ │ │ │ ├── SpringBootUrlShortenerApplication.java │ │ │ │ └── ApplicationProperties.java │ │ └── resources │ │ │ ├── db │ │ │ └── migration │ │ │ │ ├── V3__update_user_passwords.sql │ │ │ │ └── V1__create_tables.sql │ │ │ ├── static │ │ │ └── css │ │ │ │ └── styles.css │ │ │ ├── templates │ │ │ └── error │ │ │ │ ├── 404.html │ │ │ │ └── 500.html │ │ │ └── application.properties │ └── test │ │ └── java │ │ └── com │ │ └── sivalabs │ │ └── urlshortener │ │ ├── SpringBootUrlShortenerApplicationTests.java │ │ └── PasswordUtility.java ├── README.md ├── compose.yaml ├── .gitignore └── .mvn │ └── wrapper │ └── maven-wrapper.properties ├── spring-boot-url-shortener-final ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── sivalabs │ │ │ │ └── urlshortener │ │ │ │ ├── domain │ │ │ │ ├── models │ │ │ │ │ ├── Role.java │ │ │ │ │ ├── CreateUserCmd.java │ │ │ │ │ ├── CreateShortUrlCmd.java │ │ │ │ │ ├── UserDto.java │ │ │ │ │ ├── ShortUrlDto.java │ │ │ │ │ └── PagedResult.java │ │ │ │ ├── exceptions │ │ │ │ │ └── ShortUrlNotFoundException.java │ │ │ │ ├── repositories │ │ │ │ │ ├── UserRepository.java │ │ │ │ │ └── ShortUrlRepository.java │ │ │ │ └── services │ │ │ │ │ ├── RandomUtils.java │ │ │ │ │ ├── EntityMapper.java │ │ │ │ │ ├── UrlExistenceValidator.java │ │ │ │ │ └── SecurityUserDetailsService.java │ │ │ │ ├── web │ │ │ │ ├── dtos │ │ │ │ │ ├── CreateShortUrlForm.java │ │ │ │ │ └── RegisterUserRequest.java │ │ │ │ ├── GlobalExceptionHandler.java │ │ │ │ └── controllers │ │ │ │ │ └── SecurityUtils.java │ │ │ │ ├── SpringBootUrlShortenerApplication.java │ │ │ │ └── ApplicationProperties.java │ │ └── resources │ │ │ ├── db │ │ │ └── migration │ │ │ │ ├── V3__update_user_passwords.sql │ │ │ │ └── V1__create_tables.sql │ │ │ ├── static │ │ │ └── css │ │ │ │ └── styles.css │ │ │ ├── templates │ │ │ └── error │ │ │ │ ├── 404.html │ │ │ │ └── 500.html │ │ │ └── application.properties │ └── test │ │ └── java │ │ └── com │ │ └── sivalabs │ │ └── urlshortener │ │ ├── TestSpringBootUrlShortenerApplication.java │ │ ├── SpringBootUrlShortenerApplicationTests.java │ │ ├── PasswordUtility.java │ │ └── TestcontainersConfiguration.java ├── compose.yaml ├── .gitignore ├── docker │ └── compose.yaml ├── README.md └── .mvn │ └── wrapper │ └── maven-wrapper.properties ├── .gitignore └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | /mvnw text eol=lf 2 | *.cmd text eol=crlf 3 | -------------------------------------------------------------------------------- /part-2/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=spring-boot-url-shortener 2 | -------------------------------------------------------------------------------- /part-1/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sivaprasadreddy/spring-boot-url-shortener/HEAD/part-1/home.png -------------------------------------------------------------------------------- /part-1/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sivaprasadreddy/spring-boot-url-shortener/HEAD/part-1/login.png -------------------------------------------------------------------------------- /part-1/my-urls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sivaprasadreddy/spring-boot-url-shortener/HEAD/part-1/my-urls.png -------------------------------------------------------------------------------- /part-1/register.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sivaprasadreddy/spring-boot-url-shortener/HEAD/part-1/register.png -------------------------------------------------------------------------------- /part-1/admin-dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sivaprasadreddy/spring-boot-url-shortener/HEAD/part-1/admin-dashboard.png -------------------------------------------------------------------------------- /springboot-url-shortener.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sivaprasadreddy/spring-boot-url-shortener/HEAD/springboot-url-shortener.png -------------------------------------------------------------------------------- /part-11/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot URL Shortener - Part 11 2 | 3 | * Implement User Registration 4 | * Use JdbcClient for UserRepository 5 | -------------------------------------------------------------------------------- /part-10/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot URL Shortener - Part 10 2 | 3 | * Implement My URLs and Admin Dashboard Pages 4 | * Using Method Level Security 5 | * Role Hierarchy 6 | -------------------------------------------------------------------------------- /part-6/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot URL Shortener - Part 6 2 | 3 | * Implement Redirect Short URL to Original URL 4 | * Global Exception Handling using @ControllerAdvice 5 | -------------------------------------------------------------------------------- /part-10/src/main/java/com/sivalabs/urlshortener/domain/models/Role.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | public enum Role { 4 | ROLE_USER, ROLE_ADMIN 5 | } 6 | -------------------------------------------------------------------------------- /part-11/src/main/java/com/sivalabs/urlshortener/domain/models/Role.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | public enum Role { 4 | ROLE_USER, ROLE_ADMIN 5 | } 6 | -------------------------------------------------------------------------------- /part-12/src/main/java/com/sivalabs/urlshortener/domain/models/Role.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | public enum Role { 4 | ROLE_USER, ROLE_ADMIN 5 | } 6 | -------------------------------------------------------------------------------- /part-3/src/main/java/com/sivalabs/urlshortener/domain/models/Role.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | public enum Role { 4 | ROLE_USER, ROLE_ADMIN 5 | } 6 | -------------------------------------------------------------------------------- /part-4/src/main/java/com/sivalabs/urlshortener/domain/models/Role.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | public enum Role { 4 | ROLE_USER, ROLE_ADMIN 5 | } 6 | -------------------------------------------------------------------------------- /part-5/src/main/java/com/sivalabs/urlshortener/domain/models/Role.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | public enum Role { 4 | ROLE_USER, ROLE_ADMIN 5 | } 6 | -------------------------------------------------------------------------------- /part-6/src/main/java/com/sivalabs/urlshortener/domain/models/Role.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | public enum Role { 4 | ROLE_USER, ROLE_ADMIN 5 | } 6 | -------------------------------------------------------------------------------- /part-7/src/main/java/com/sivalabs/urlshortener/domain/models/Role.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | public enum Role { 4 | ROLE_USER, ROLE_ADMIN 5 | } 6 | -------------------------------------------------------------------------------- /part-8/src/main/java/com/sivalabs/urlshortener/domain/models/Role.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | public enum Role { 4 | ROLE_USER, ROLE_ADMIN 5 | } 6 | -------------------------------------------------------------------------------- /part-9/src/main/java/com/sivalabs/urlshortener/domain/models/Role.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | public enum Role { 4 | ROLE_USER, ROLE_ADMIN 5 | } 6 | -------------------------------------------------------------------------------- /part-5/src/main/java/com/sivalabs/urlshortener/domain/models/CreateShortUrlCmd.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | public record CreateShortUrlCmd(String originalUrl) { 4 | } 5 | -------------------------------------------------------------------------------- /part-6/src/main/java/com/sivalabs/urlshortener/domain/models/CreateShortUrlCmd.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | public record CreateShortUrlCmd(String originalUrl) { 4 | } 5 | -------------------------------------------------------------------------------- /part-7/src/main/java/com/sivalabs/urlshortener/domain/models/CreateShortUrlCmd.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | public record CreateShortUrlCmd(String originalUrl) { 4 | } 5 | -------------------------------------------------------------------------------- /part-5/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot URL Shortener - Part 5 2 | 3 | * Implement Create Short URL as a Guest User 4 | * Form Submission and Validation 5 | * Redirect Attributes 6 | * Post-Redirect-Get Pattern 7 | 8 | -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/main/java/com/sivalabs/urlshortener/domain/models/Role.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | public enum Role { 4 | ROLE_USER, ROLE_ADMIN 5 | } 6 | -------------------------------------------------------------------------------- /part-4/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot URL Shortener - Part 4 2 | 3 | * Handle LazyLoadingException 4 | * Understanding OSIV Filter 5 | * Loading Lazy associations 6 | * JPQL 7 | * Entity Graph 8 | * JPA Best Practices 9 | -------------------------------------------------------------------------------- /part-8/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot URL Shortener - Part 8 2 | 3 | * Update Create ShortURL feature: 4 | * Add IsPrivate, ExpiryInDays 5 | * Update RedirectToOriginalURL feature: 6 | * Check for private Short URLs 7 | -------------------------------------------------------------------------------- /part-9/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot URL Shortener - Part 9 2 | 3 | * Understand Spring Data JPA Pagination support 4 | * Using @PageableDefault for pagination 5 | * Pagination starting page number with 1 6 | * Creating Thymeleaf Pagination fragment -------------------------------------------------------------------------------- /part-10/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | postgres: 3 | image: 'postgres:17' 4 | environment: 5 | - 'POSTGRES_DB=postgres' 6 | - 'POSTGRES_PASSWORD=postgres' 7 | - 'POSTGRES_USER=postgres' 8 | ports: 9 | - '5432:5432' 10 | -------------------------------------------------------------------------------- /part-11/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | postgres: 3 | image: 'postgres:17' 4 | environment: 5 | - 'POSTGRES_DB=postgres' 6 | - 'POSTGRES_PASSWORD=postgres' 7 | - 'POSTGRES_USER=postgres' 8 | ports: 9 | - '5432:5432' 10 | -------------------------------------------------------------------------------- /part-12/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | postgres: 3 | image: 'postgres:17' 4 | environment: 5 | - 'POSTGRES_DB=postgres' 6 | - 'POSTGRES_PASSWORD=postgres' 7 | - 'POSTGRES_USER=postgres' 8 | ports: 9 | - '5432:5432' 10 | -------------------------------------------------------------------------------- /part-3/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | postgres: 3 | image: 'postgres:17' 4 | environment: 5 | - 'POSTGRES_DB=postgres' 6 | - 'POSTGRES_PASSWORD=postgres' 7 | - 'POSTGRES_USER=postgres' 8 | ports: 9 | - '5432:5432' 10 | -------------------------------------------------------------------------------- /part-4/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | postgres: 3 | image: 'postgres:17' 4 | environment: 5 | - 'POSTGRES_DB=postgres' 6 | - 'POSTGRES_PASSWORD=postgres' 7 | - 'POSTGRES_USER=postgres' 8 | ports: 9 | - '5432:5432' 10 | -------------------------------------------------------------------------------- /part-5/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | postgres: 3 | image: 'postgres:17' 4 | environment: 5 | - 'POSTGRES_DB=postgres' 6 | - 'POSTGRES_PASSWORD=postgres' 7 | - 'POSTGRES_USER=postgres' 8 | ports: 9 | - '5432:5432' 10 | -------------------------------------------------------------------------------- /part-6/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | postgres: 3 | image: 'postgres:17' 4 | environment: 5 | - 'POSTGRES_DB=postgres' 6 | - 'POSTGRES_PASSWORD=postgres' 7 | - 'POSTGRES_USER=postgres' 8 | ports: 9 | - '5432:5432' 10 | -------------------------------------------------------------------------------- /part-7/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | postgres: 3 | image: 'postgres:17' 4 | environment: 5 | - 'POSTGRES_DB=postgres' 6 | - 'POSTGRES_PASSWORD=postgres' 7 | - 'POSTGRES_USER=postgres' 8 | ports: 9 | - '5432:5432' 10 | -------------------------------------------------------------------------------- /part-8/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | postgres: 3 | image: 'postgres:17' 4 | environment: 5 | - 'POSTGRES_DB=postgres' 6 | - 'POSTGRES_PASSWORD=postgres' 7 | - 'POSTGRES_USER=postgres' 8 | ports: 9 | - '5432:5432' 10 | -------------------------------------------------------------------------------- /part-9/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | postgres: 3 | image: 'postgres:17' 4 | environment: 5 | - 'POSTGRES_DB=postgres' 6 | - 'POSTGRES_PASSWORD=postgres' 7 | - 'POSTGRES_USER=postgres' 8 | ports: 9 | - '5432:5432' 10 | -------------------------------------------------------------------------------- /part-11/src/main/java/com/sivalabs/urlshortener/domain/models/CreateUserCmd.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | public record CreateUserCmd( 4 | String email, 5 | String password, 6 | String name, 7 | Role role) { 8 | } 9 | -------------------------------------------------------------------------------- /part-12/src/main/java/com/sivalabs/urlshortener/domain/models/CreateUserCmd.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | public record CreateUserCmd( 4 | String email, 5 | String password, 6 | String name, 7 | Role role) { 8 | } 9 | -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | postgres: 3 | image: 'postgres:17' 4 | environment: 5 | - 'POSTGRES_DB=postgres' 6 | - 'POSTGRES_PASSWORD=postgres' 7 | - 'POSTGRES_USER=postgres' 8 | ports: 9 | - '5432:5432' 10 | -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/main/java/com/sivalabs/urlshortener/domain/models/CreateUserCmd.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | public record CreateUserCmd( 4 | String email, 5 | String password, 6 | String name, 7 | Role role) { 8 | } 9 | -------------------------------------------------------------------------------- /part-10/src/main/resources/db/migration/V3__update_user_passwords.sql: -------------------------------------------------------------------------------- 1 | update users set password='$2a$10$Vq7kPWnWwz7isGGE/8gxJOLdZCRFsBzqU6swEprp66kGvK1UdbQjy' where email='admin@gmail.com'; 2 | update users set password='$2a$10$FjomyZihHtoW7sUFuypX2u3p.orW5nuYOpNA8iK9FPAp1KeFY2iyi' where email='siva@gmail.com'; 3 | -------------------------------------------------------------------------------- /part-11/src/main/resources/db/migration/V3__update_user_passwords.sql: -------------------------------------------------------------------------------- 1 | update users set password='$2a$10$Vq7kPWnWwz7isGGE/8gxJOLdZCRFsBzqU6swEprp66kGvK1UdbQjy' where email='admin@gmail.com'; 2 | update users set password='$2a$10$FjomyZihHtoW7sUFuypX2u3p.orW5nuYOpNA8iK9FPAp1KeFY2iyi' where email='siva@gmail.com'; 3 | -------------------------------------------------------------------------------- /part-12/src/main/resources/db/migration/V3__update_user_passwords.sql: -------------------------------------------------------------------------------- 1 | update users set password='$2a$10$Vq7kPWnWwz7isGGE/8gxJOLdZCRFsBzqU6swEprp66kGvK1UdbQjy' where email='admin@gmail.com'; 2 | update users set password='$2a$10$FjomyZihHtoW7sUFuypX2u3p.orW5nuYOpNA8iK9FPAp1KeFY2iyi' where email='siva@gmail.com'; 3 | -------------------------------------------------------------------------------- /part-7/src/main/resources/db/migration/V3__update_user_passwords.sql: -------------------------------------------------------------------------------- 1 | update users set password='$2a$10$Vq7kPWnWwz7isGGE/8gxJOLdZCRFsBzqU6swEprp66kGvK1UdbQjy' where email='admin@gmail.com'; 2 | update users set password='$2a$10$FjomyZihHtoW7sUFuypX2u3p.orW5nuYOpNA8iK9FPAp1KeFY2iyi' where email='siva@gmail.com'; 3 | -------------------------------------------------------------------------------- /part-8/src/main/resources/db/migration/V3__update_user_passwords.sql: -------------------------------------------------------------------------------- 1 | update users set password='$2a$10$Vq7kPWnWwz7isGGE/8gxJOLdZCRFsBzqU6swEprp66kGvK1UdbQjy' where email='admin@gmail.com'; 2 | update users set password='$2a$10$FjomyZihHtoW7sUFuypX2u3p.orW5nuYOpNA8iK9FPAp1KeFY2iyi' where email='siva@gmail.com'; 3 | -------------------------------------------------------------------------------- /part-9/src/main/resources/db/migration/V3__update_user_passwords.sql: -------------------------------------------------------------------------------- 1 | update users set password='$2a$10$Vq7kPWnWwz7isGGE/8gxJOLdZCRFsBzqU6swEprp66kGvK1UdbQjy' where email='admin@gmail.com'; 2 | update users set password='$2a$10$FjomyZihHtoW7sUFuypX2u3p.orW5nuYOpNA8iK9FPAp1KeFY2iyi' where email='siva@gmail.com'; 3 | -------------------------------------------------------------------------------- /part-10/src/main/java/com/sivalabs/urlshortener/domain/models/CreateShortUrlCmd.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | public record CreateShortUrlCmd( 4 | String originalUrl, 5 | Boolean isPrivate, 6 | Integer expirationInDays, 7 | Long userId 8 | ) { 9 | } 10 | -------------------------------------------------------------------------------- /part-11/src/main/java/com/sivalabs/urlshortener/domain/models/CreateShortUrlCmd.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | public record CreateShortUrlCmd( 4 | String originalUrl, 5 | Boolean isPrivate, 6 | Integer expirationInDays, 7 | Long userId 8 | ) { 9 | } 10 | -------------------------------------------------------------------------------- /part-12/src/main/java/com/sivalabs/urlshortener/domain/models/CreateShortUrlCmd.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | public record CreateShortUrlCmd( 4 | String originalUrl, 5 | Boolean isPrivate, 6 | Integer expirationInDays, 7 | Long userId 8 | ) { 9 | } 10 | -------------------------------------------------------------------------------- /part-8/src/main/java/com/sivalabs/urlshortener/domain/models/CreateShortUrlCmd.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | public record CreateShortUrlCmd( 4 | String originalUrl, 5 | Boolean isPrivate, 6 | Integer expirationInDays, 7 | Long userId 8 | ) { 9 | } 10 | -------------------------------------------------------------------------------- /part-9/src/main/java/com/sivalabs/urlshortener/domain/models/CreateShortUrlCmd.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | public record CreateShortUrlCmd( 4 | String originalUrl, 5 | Boolean isPrivate, 6 | Integer expirationInDays, 7 | Long userId 8 | ) { 9 | } 10 | -------------------------------------------------------------------------------- /part-10/src/main/java/com/sivalabs/urlshortener/domain/exceptions/ShortUrlNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.exceptions; 2 | 3 | public class ShortUrlNotFoundException extends RuntimeException { 4 | public ShortUrlNotFoundException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /part-10/src/main/java/com/sivalabs/urlshortener/domain/models/UserDto.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * DTO for {@link com.sivalabs.urlshortener.domain.entities.User} 7 | */ 8 | public record UserDto(Long id, String name) implements Serializable { 9 | } -------------------------------------------------------------------------------- /part-11/src/main/java/com/sivalabs/urlshortener/domain/exceptions/ShortUrlNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.exceptions; 2 | 3 | public class ShortUrlNotFoundException extends RuntimeException { 4 | public ShortUrlNotFoundException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /part-11/src/main/java/com/sivalabs/urlshortener/domain/models/UserDto.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * DTO for {@link com.sivalabs.urlshortener.domain.entities.User} 7 | */ 8 | public record UserDto(Long id, String name) implements Serializable { 9 | } -------------------------------------------------------------------------------- /part-12/src/main/java/com/sivalabs/urlshortener/domain/exceptions/ShortUrlNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.exceptions; 2 | 3 | public class ShortUrlNotFoundException extends RuntimeException { 4 | public ShortUrlNotFoundException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /part-12/src/main/java/com/sivalabs/urlshortener/domain/models/UserDto.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * DTO for {@link com.sivalabs.urlshortener.domain.entities.User} 7 | */ 8 | public record UserDto(Long id, String name) implements Serializable { 9 | } -------------------------------------------------------------------------------- /part-4/src/main/java/com/sivalabs/urlshortener/domain/models/UserDto.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * DTO for {@link com.sivalabs.urlshortener.domain.entities.User} 7 | */ 8 | public record UserDto(Long id, String name) implements Serializable { 9 | } -------------------------------------------------------------------------------- /part-5/src/main/java/com/sivalabs/urlshortener/domain/models/UserDto.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * DTO for {@link com.sivalabs.urlshortener.domain.entities.User} 7 | */ 8 | public record UserDto(Long id, String name) implements Serializable { 9 | } -------------------------------------------------------------------------------- /part-6/src/main/java/com/sivalabs/urlshortener/domain/exceptions/ShortUrlNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.exceptions; 2 | 3 | public class ShortUrlNotFoundException extends RuntimeException { 4 | public ShortUrlNotFoundException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /part-6/src/main/java/com/sivalabs/urlshortener/domain/models/UserDto.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * DTO for {@link com.sivalabs.urlshortener.domain.entities.User} 7 | */ 8 | public record UserDto(Long id, String name) implements Serializable { 9 | } -------------------------------------------------------------------------------- /part-7/src/main/java/com/sivalabs/urlshortener/domain/exceptions/ShortUrlNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.exceptions; 2 | 3 | public class ShortUrlNotFoundException extends RuntimeException { 4 | public ShortUrlNotFoundException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /part-7/src/main/java/com/sivalabs/urlshortener/domain/models/UserDto.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * DTO for {@link com.sivalabs.urlshortener.domain.entities.User} 7 | */ 8 | public record UserDto(Long id, String name) implements Serializable { 9 | } -------------------------------------------------------------------------------- /part-8/src/main/java/com/sivalabs/urlshortener/domain/exceptions/ShortUrlNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.exceptions; 2 | 3 | public class ShortUrlNotFoundException extends RuntimeException { 4 | public ShortUrlNotFoundException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /part-8/src/main/java/com/sivalabs/urlshortener/domain/models/UserDto.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * DTO for {@link com.sivalabs.urlshortener.domain.entities.User} 7 | */ 8 | public record UserDto(Long id, String name) implements Serializable { 9 | } -------------------------------------------------------------------------------- /part-9/src/main/java/com/sivalabs/urlshortener/domain/exceptions/ShortUrlNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.exceptions; 2 | 3 | public class ShortUrlNotFoundException extends RuntimeException { 4 | public ShortUrlNotFoundException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /part-9/src/main/java/com/sivalabs/urlshortener/domain/models/UserDto.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * DTO for {@link com.sivalabs.urlshortener.domain.entities.User} 7 | */ 8 | public record UserDto(Long id, String name) implements Serializable { 9 | } -------------------------------------------------------------------------------- /part-5/src/main/java/com/sivalabs/urlshortener/web/dtos/CreateShortUrlForm.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web.dtos; 2 | 3 | 4 | import jakarta.validation.constraints.NotBlank; 5 | 6 | public record CreateShortUrlForm( 7 | @NotBlank(message = "Original URL is required") 8 | String originalUrl) { 9 | } 10 | -------------------------------------------------------------------------------- /part-6/src/main/java/com/sivalabs/urlshortener/web/dtos/CreateShortUrlForm.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web.dtos; 2 | 3 | 4 | import jakarta.validation.constraints.NotBlank; 5 | 6 | public record CreateShortUrlForm( 7 | @NotBlank(message = "Original URL is required") 8 | String originalUrl) { 9 | } 10 | -------------------------------------------------------------------------------- /part-7/src/main/java/com/sivalabs/urlshortener/web/dtos/CreateShortUrlForm.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web.dtos; 2 | 3 | 4 | import jakarta.validation.constraints.NotBlank; 5 | 6 | public record CreateShortUrlForm( 7 | @NotBlank(message = "Original URL is required") 8 | String originalUrl) { 9 | } 10 | -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/main/resources/db/migration/V3__update_user_passwords.sql: -------------------------------------------------------------------------------- 1 | update users set password='$2a$10$Vq7kPWnWwz7isGGE/8gxJOLdZCRFsBzqU6swEprp66kGvK1UdbQjy' where email='admin@gmail.com'; 2 | update users set password='$2a$10$FjomyZihHtoW7sUFuypX2u3p.orW5nuYOpNA8iK9FPAp1KeFY2iyi' where email='siva@gmail.com'; 3 | -------------------------------------------------------------------------------- /part-7/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot URL Shortener - Part 7 2 | 3 | * Securing WebApp using Spring Security 4 | * Update users plain text password to BCrypt encoded text 5 | * Default Login Page 6 | * Custom Login Page 7 | * Show Login/Register/Logout conditionally 8 | * Role Based Access Control (RBAC) 9 | * Get Current Login User Info 10 | -------------------------------------------------------------------------------- /part-12/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot URL Shortener - Part 12 2 | 3 | * Dockerizing Spring Boot Application 4 | * Run URL Shortener using Docker Compose 5 | 6 | ```shell 7 | # To build Docker image 8 | $ ./mvnw spring-boot:build-image 9 | 10 | # To run the application using Docker Compose 11 | $ cd docker 12 | $ docker compose up -d 13 | ``` -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/main/java/com/sivalabs/urlshortener/domain/models/CreateShortUrlCmd.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | public record CreateShortUrlCmd( 4 | String originalUrl, 5 | Boolean isPrivate, 6 | Integer expirationInDays, 7 | Long userId 8 | ) { 9 | } 10 | -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/main/java/com/sivalabs/urlshortener/domain/exceptions/ShortUrlNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.exceptions; 2 | 3 | public class ShortUrlNotFoundException extends RuntimeException { 4 | public ShortUrlNotFoundException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/main/java/com/sivalabs/urlshortener/domain/models/UserDto.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * DTO for {@link com.sivalabs.urlshortener.domain.entities.User} 7 | */ 8 | public record UserDto(Long id, String name) implements Serializable { 9 | } -------------------------------------------------------------------------------- /part-10/src/test/java/com/sivalabs/urlshortener/SpringBootUrlShortenerApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class SpringBootUrlShortenerApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part-11/src/test/java/com/sivalabs/urlshortener/SpringBootUrlShortenerApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class SpringBootUrlShortenerApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part-2/src/test/java/com/sivalabs/urlshortener/SpringBootUrlShortenerApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class SpringBootUrlShortenerApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part-3/src/test/java/com/sivalabs/urlshortener/SpringBootUrlShortenerApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class SpringBootUrlShortenerApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part-4/src/test/java/com/sivalabs/urlshortener/SpringBootUrlShortenerApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class SpringBootUrlShortenerApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part-5/src/test/java/com/sivalabs/urlshortener/SpringBootUrlShortenerApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class SpringBootUrlShortenerApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part-6/src/test/java/com/sivalabs/urlshortener/SpringBootUrlShortenerApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class SpringBootUrlShortenerApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part-7/src/test/java/com/sivalabs/urlshortener/SpringBootUrlShortenerApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class SpringBootUrlShortenerApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part-8/src/test/java/com/sivalabs/urlshortener/SpringBootUrlShortenerApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class SpringBootUrlShortenerApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part-9/src/test/java/com/sivalabs/urlshortener/SpringBootUrlShortenerApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class SpringBootUrlShortenerApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part-3/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=spring-boot-url-shortener 2 | 3 | spring.docker.compose.lifecycle-management=start_only 4 | 5 | spring.datasource.url=jdbc:postgresql://localhost:5432/postgres 6 | spring.datasource.username=postgres 7 | spring.datasource.password=postgres 8 | spring.jpa.show-sql=true 9 | #spring.jpa.hibernate.ddl-auto=update 10 | -------------------------------------------------------------------------------- /part-2/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot URL Shortener - Part 2 2 | 3 | * Create Spring Boot Project 4 | * Understand Spring Boot project structure 5 | * Learn how to handle Web Requests 6 | * Difference between @Controller and @RestController 7 | * Render HTML page 8 | * Using Thymeleaf to render HTML Templates with Dynamic Data 9 | * Create Layout Template 10 | * Using WebJars for loading static resources -------------------------------------------------------------------------------- /part-4/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=spring-boot-url-shortener 2 | 3 | spring.docker.compose.lifecycle-management=start_only 4 | 5 | spring.datasource.url=jdbc:postgresql://localhost:5432/postgres 6 | spring.datasource.username=postgres 7 | spring.datasource.password=postgres 8 | spring.jpa.show-sql=true 9 | #spring.jpa.hibernate.ddl-auto=update 10 | spring.jpa.open-in-view=false -------------------------------------------------------------------------------- /part-10/src/main/java/com/sivalabs/urlshortener/domain/repositories/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.repositories; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.User; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.Optional; 7 | 8 | public interface UserRepository extends JpaRepository { 9 | Optional findByEmail(String email); 10 | } -------------------------------------------------------------------------------- /part-7/src/main/java/com/sivalabs/urlshortener/domain/repositories/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.repositories; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.User; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.Optional; 7 | 8 | public interface UserRepository extends JpaRepository { 9 | Optional findByEmail(String email); 10 | } -------------------------------------------------------------------------------- /part-8/src/main/java/com/sivalabs/urlshortener/domain/repositories/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.repositories; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.User; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.Optional; 7 | 8 | public interface UserRepository extends JpaRepository { 9 | Optional findByEmail(String email); 10 | } -------------------------------------------------------------------------------- /part-9/src/main/java/com/sivalabs/urlshortener/domain/repositories/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.repositories; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.User; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.Optional; 7 | 8 | public interface UserRepository extends JpaRepository { 9 | Optional findByEmail(String email); 10 | } -------------------------------------------------------------------------------- /part-12/src/test/java/com/sivalabs/urlshortener/TestSpringBootUrlShortenerApplication.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | 5 | class TestSpringBootUrlShortenerApplication { 6 | 7 | public static void main(String[] args) { 8 | SpringApplication 9 | .from(SpringBootUrlShortenerApplication::main) 10 | .with(TestcontainersConfiguration.class) 11 | .run(args); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /part-3/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot URL Shortener - Part 3 2 | 3 | * Implement Public ShortURLs List View in Home Page 4 | * Add Spring Data JPA support with H2/PostgreSQL 5 | * Initializing database: 6 | * JPA ddl-auto 7 | * schema.sql, data.sql 8 | * Flyway Migrations 9 | * Create entities, repositories 10 | * Generate easily using IntelliJ IDEA Ultimate 11 | * Implement Controller and Service logic 12 | * Using Spring Boot Docker Compose support 13 | -------------------------------------------------------------------------------- /part-10/src/main/resources/static/css/styles.css: -------------------------------------------------------------------------------- 1 | 2 | html, body { 3 | height: 100%; 4 | } 5 | 6 | body { 7 | padding-top: 60px; 8 | padding-bottom: 40px; 9 | display: flex; 10 | flex-direction: column; 11 | min-height: 100vh; 12 | } 13 | 14 | .navbar-brand { 15 | font-weight: bold; 16 | font-size: 1.5rem; 17 | } 18 | 19 | body > .container { 20 | flex: 1 0 auto; 21 | display: flex; 22 | flex-direction: column; 23 | } 24 | -------------------------------------------------------------------------------- /part-11/src/main/resources/static/css/styles.css: -------------------------------------------------------------------------------- 1 | 2 | html, body { 3 | height: 100%; 4 | } 5 | 6 | body { 7 | padding-top: 60px; 8 | padding-bottom: 40px; 9 | display: flex; 10 | flex-direction: column; 11 | min-height: 100vh; 12 | } 13 | 14 | .navbar-brand { 15 | font-weight: bold; 16 | font-size: 1.5rem; 17 | } 18 | 19 | body > .container { 20 | flex: 1 0 auto; 21 | display: flex; 22 | flex-direction: column; 23 | } 24 | -------------------------------------------------------------------------------- /part-12/src/main/resources/static/css/styles.css: -------------------------------------------------------------------------------- 1 | 2 | html, body { 3 | height: 100%; 4 | } 5 | 6 | body { 7 | padding-top: 60px; 8 | padding-bottom: 40px; 9 | display: flex; 10 | flex-direction: column; 11 | min-height: 100vh; 12 | } 13 | 14 | .navbar-brand { 15 | font-weight: bold; 16 | font-size: 1.5rem; 17 | } 18 | 19 | body > .container { 20 | flex: 1 0 auto; 21 | display: flex; 22 | flex-direction: column; 23 | } 24 | -------------------------------------------------------------------------------- /part-2/src/main/resources/static/css/styles.css: -------------------------------------------------------------------------------- 1 | 2 | html, body { 3 | height: 100%; 4 | } 5 | 6 | body { 7 | padding-top: 60px; 8 | padding-bottom: 40px; 9 | display: flex; 10 | flex-direction: column; 11 | min-height: 100vh; 12 | } 13 | 14 | .navbar-brand { 15 | font-weight: bold; 16 | font-size: 1.5rem; 17 | } 18 | 19 | body > .container { 20 | flex: 1 0 auto; 21 | display: flex; 22 | flex-direction: column; 23 | } 24 | -------------------------------------------------------------------------------- /part-3/src/main/resources/static/css/styles.css: -------------------------------------------------------------------------------- 1 | 2 | html, body { 3 | height: 100%; 4 | } 5 | 6 | body { 7 | padding-top: 60px; 8 | padding-bottom: 40px; 9 | display: flex; 10 | flex-direction: column; 11 | min-height: 100vh; 12 | } 13 | 14 | .navbar-brand { 15 | font-weight: bold; 16 | font-size: 1.5rem; 17 | } 18 | 19 | body > .container { 20 | flex: 1 0 auto; 21 | display: flex; 22 | flex-direction: column; 23 | } 24 | -------------------------------------------------------------------------------- /part-4/src/main/resources/static/css/styles.css: -------------------------------------------------------------------------------- 1 | 2 | html, body { 3 | height: 100%; 4 | } 5 | 6 | body { 7 | padding-top: 60px; 8 | padding-bottom: 40px; 9 | display: flex; 10 | flex-direction: column; 11 | min-height: 100vh; 12 | } 13 | 14 | .navbar-brand { 15 | font-weight: bold; 16 | font-size: 1.5rem; 17 | } 18 | 19 | body > .container { 20 | flex: 1 0 auto; 21 | display: flex; 22 | flex-direction: column; 23 | } 24 | -------------------------------------------------------------------------------- /part-5/src/main/resources/static/css/styles.css: -------------------------------------------------------------------------------- 1 | 2 | html, body { 3 | height: 100%; 4 | } 5 | 6 | body { 7 | padding-top: 60px; 8 | padding-bottom: 40px; 9 | display: flex; 10 | flex-direction: column; 11 | min-height: 100vh; 12 | } 13 | 14 | .navbar-brand { 15 | font-weight: bold; 16 | font-size: 1.5rem; 17 | } 18 | 19 | body > .container { 20 | flex: 1 0 auto; 21 | display: flex; 22 | flex-direction: column; 23 | } 24 | -------------------------------------------------------------------------------- /part-6/src/main/resources/static/css/styles.css: -------------------------------------------------------------------------------- 1 | 2 | html, body { 3 | height: 100%; 4 | } 5 | 6 | body { 7 | padding-top: 60px; 8 | padding-bottom: 40px; 9 | display: flex; 10 | flex-direction: column; 11 | min-height: 100vh; 12 | } 13 | 14 | .navbar-brand { 15 | font-weight: bold; 16 | font-size: 1.5rem; 17 | } 18 | 19 | body > .container { 20 | flex: 1 0 auto; 21 | display: flex; 22 | flex-direction: column; 23 | } 24 | -------------------------------------------------------------------------------- /part-7/src/main/resources/static/css/styles.css: -------------------------------------------------------------------------------- 1 | 2 | html, body { 3 | height: 100%; 4 | } 5 | 6 | body { 7 | padding-top: 60px; 8 | padding-bottom: 40px; 9 | display: flex; 10 | flex-direction: column; 11 | min-height: 100vh; 12 | } 13 | 14 | .navbar-brand { 15 | font-weight: bold; 16 | font-size: 1.5rem; 17 | } 18 | 19 | body > .container { 20 | flex: 1 0 auto; 21 | display: flex; 22 | flex-direction: column; 23 | } 24 | -------------------------------------------------------------------------------- /part-8/src/main/resources/static/css/styles.css: -------------------------------------------------------------------------------- 1 | 2 | html, body { 3 | height: 100%; 4 | } 5 | 6 | body { 7 | padding-top: 60px; 8 | padding-bottom: 40px; 9 | display: flex; 10 | flex-direction: column; 11 | min-height: 100vh; 12 | } 13 | 14 | .navbar-brand { 15 | font-weight: bold; 16 | font-size: 1.5rem; 17 | } 18 | 19 | body > .container { 20 | flex: 1 0 auto; 21 | display: flex; 22 | flex-direction: column; 23 | } 24 | -------------------------------------------------------------------------------- /part-9/src/main/resources/static/css/styles.css: -------------------------------------------------------------------------------- 1 | 2 | html, body { 3 | height: 100%; 4 | } 5 | 6 | body { 7 | padding-top: 60px; 8 | padding-bottom: 40px; 9 | display: flex; 10 | flex-direction: column; 11 | min-height: 100vh; 12 | } 13 | 14 | .navbar-brand { 15 | font-weight: bold; 16 | font-size: 1.5rem; 17 | } 18 | 19 | body > .container { 20 | flex: 1 0 auto; 21 | display: flex; 22 | flex-direction: column; 23 | } 24 | -------------------------------------------------------------------------------- /part-12/src/test/java/com/sivalabs/urlshortener/SpringBootUrlShortenerApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | import org.springframework.context.annotation.Import; 6 | 7 | @SpringBootTest 8 | @Import(TestcontainersConfiguration.class) 9 | class SpringBootUrlShortenerApplicationTests { 10 | 11 | @Test 12 | void contextLoads() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/test/java/com/sivalabs/urlshortener/TestSpringBootUrlShortenerApplication.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | 5 | class TestSpringBootUrlShortenerApplication { 6 | 7 | public static void main(String[] args) { 8 | SpringApplication 9 | .from(SpringBootUrlShortenerApplication::main) 10 | .with(TestcontainersConfiguration.class) 11 | .run(args); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /part-2/src/main/java/com/sivalabs/urlshortener/SpringBootUrlShortenerApplication.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringBootUrlShortenerApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringBootUrlShortenerApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part-3/src/main/java/com/sivalabs/urlshortener/SpringBootUrlShortenerApplication.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringBootUrlShortenerApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringBootUrlShortenerApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /part-4/src/main/java/com/sivalabs/urlshortener/SpringBootUrlShortenerApplication.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringBootUrlShortenerApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringBootUrlShortenerApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/main/resources/static/css/styles.css: -------------------------------------------------------------------------------- 1 | 2 | html, body { 3 | height: 100%; 4 | } 5 | 6 | body { 7 | padding-top: 60px; 8 | padding-bottom: 40px; 9 | display: flex; 10 | flex-direction: column; 11 | min-height: 100vh; 12 | } 13 | 14 | .navbar-brand { 15 | font-weight: bold; 16 | font-size: 1.5rem; 17 | } 18 | 19 | body > .container { 20 | flex: 1 0 auto; 21 | display: flex; 22 | flex-direction: column; 23 | } 24 | -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/main/java/com/sivalabs/urlshortener/domain/repositories/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.repositories; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.User; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.Optional; 7 | 8 | public interface UserRepository extends JpaRepository { 9 | Optional findByEmail(String email); 10 | boolean existsByEmail(String email); 11 | } -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/test/java/com/sivalabs/urlshortener/SpringBootUrlShortenerApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | import org.springframework.context.annotation.Import; 6 | 7 | @SpringBootTest 8 | @Import(TestcontainersConfiguration.class) 9 | class SpringBootUrlShortenerApplicationTests { 10 | 11 | @Test 12 | void contextLoads() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /part-2/src/main/resources/templates/about.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | About 10 | 11 | 12 |
13 |

URL Shortener About Page

14 |
15 | 16 | -------------------------------------------------------------------------------- /part-10/src/main/resources/templates/error/404.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | Page Not Found 10 | 11 | 12 |
13 |

Request Short URL Not Found or Expired

14 |
15 | 16 | -------------------------------------------------------------------------------- /part-11/src/main/resources/templates/error/404.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | Page Not Found 10 | 11 | 12 |
13 |

Request Short URL Not Found or Expired

14 |
15 | 16 | -------------------------------------------------------------------------------- /part-12/src/main/resources/templates/error/404.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | Page Not Found 10 | 11 | 12 |
13 |

Request Short URL Not Found or Expired

14 |
15 | 16 | -------------------------------------------------------------------------------- /part-6/src/main/resources/templates/error/404.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | Page Not Found 10 | 11 | 12 |
13 |

Request Short URL Not Found or Expired

14 |
15 | 16 | -------------------------------------------------------------------------------- /part-7/src/main/resources/templates/error/404.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | Page Not Found 10 | 11 | 12 |
13 |

Request Short URL Not Found or Expired

14 |
15 | 16 | -------------------------------------------------------------------------------- /part-8/src/main/resources/templates/error/404.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | Page Not Found 10 | 11 | 12 |
13 |

Request Short URL Not Found or Expired

14 |
15 | 16 | -------------------------------------------------------------------------------- /part-9/src/main/resources/templates/error/404.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | Page Not Found 10 | 11 | 12 |
13 |

Request Short URL Not Found or Expired

14 |
15 | 16 | -------------------------------------------------------------------------------- /part-10/src/test/java/com/sivalabs/urlshortener/PasswordUtility.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 4 | import org.springframework.security.crypto.password.PasswordEncoder; 5 | 6 | public class PasswordUtility { 7 | public static void main(String[] args) { 8 | PasswordEncoder encoder = new BCryptPasswordEncoder(); 9 | System.out.println(encoder.encode("secret")); 10 | System.out.println(encoder.encode("admin")); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /part-11/src/test/java/com/sivalabs/urlshortener/PasswordUtility.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 4 | import org.springframework.security.crypto.password.PasswordEncoder; 5 | 6 | public class PasswordUtility { 7 | public static void main(String[] args) { 8 | PasswordEncoder encoder = new BCryptPasswordEncoder(); 9 | System.out.println(encoder.encode("secret")); 10 | System.out.println(encoder.encode("admin")); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /part-12/src/test/java/com/sivalabs/urlshortener/PasswordUtility.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 4 | import org.springframework.security.crypto.password.PasswordEncoder; 5 | 6 | public class PasswordUtility { 7 | public static void main(String[] args) { 8 | PasswordEncoder encoder = new BCryptPasswordEncoder(); 9 | System.out.println(encoder.encode("secret")); 10 | System.out.println(encoder.encode("admin")); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /part-7/src/test/java/com/sivalabs/urlshortener/PasswordUtility.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 4 | import org.springframework.security.crypto.password.PasswordEncoder; 5 | 6 | public class PasswordUtility { 7 | public static void main(String[] args) { 8 | PasswordEncoder encoder = new BCryptPasswordEncoder(); 9 | System.out.println(encoder.encode("secret")); 10 | System.out.println(encoder.encode("admin")); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /part-8/src/test/java/com/sivalabs/urlshortener/PasswordUtility.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 4 | import org.springframework.security.crypto.password.PasswordEncoder; 5 | 6 | public class PasswordUtility { 7 | public static void main(String[] args) { 8 | PasswordEncoder encoder = new BCryptPasswordEncoder(); 9 | System.out.println(encoder.encode("secret")); 10 | System.out.println(encoder.encode("admin")); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /part-9/src/test/java/com/sivalabs/urlshortener/PasswordUtility.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 4 | import org.springframework.security.crypto.password.PasswordEncoder; 5 | 6 | public class PasswordUtility { 7 | public static void main(String[] args) { 8 | PasswordEncoder encoder = new BCryptPasswordEncoder(); 9 | System.out.println(encoder.encode("secret")); 10 | System.out.println(encoder.encode("admin")); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /part-10/src/main/java/com/sivalabs/urlshortener/web/dtos/CreateShortUrlForm.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web.dtos; 2 | 3 | 4 | import jakarta.validation.constraints.Max; 5 | import jakarta.validation.constraints.Min; 6 | import jakarta.validation.constraints.NotBlank; 7 | 8 | public record CreateShortUrlForm( 9 | @NotBlank(message = "Original URL is required") 10 | String originalUrl, 11 | Boolean isPrivate, 12 | @Min(1) 13 | @Max(365) 14 | Integer expirationInDays 15 | ) { 16 | } 17 | -------------------------------------------------------------------------------- /part-10/src/main/resources/templates/error/500.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | Server Error 10 | 11 | 12 |
13 |

Some Error Occurred While Processing Your Request

14 |
15 | 16 | -------------------------------------------------------------------------------- /part-11/src/main/java/com/sivalabs/urlshortener/web/dtos/CreateShortUrlForm.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web.dtos; 2 | 3 | 4 | import jakarta.validation.constraints.Max; 5 | import jakarta.validation.constraints.Min; 6 | import jakarta.validation.constraints.NotBlank; 7 | 8 | public record CreateShortUrlForm( 9 | @NotBlank(message = "Original URL is required") 10 | String originalUrl, 11 | Boolean isPrivate, 12 | @Min(1) 13 | @Max(365) 14 | Integer expirationInDays 15 | ) { 16 | } 17 | -------------------------------------------------------------------------------- /part-11/src/main/resources/templates/error/500.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | Server Error 10 | 11 | 12 |
13 |

Some Error Occurred While Processing Your Request

14 |
15 | 16 | -------------------------------------------------------------------------------- /part-12/src/main/java/com/sivalabs/urlshortener/web/dtos/CreateShortUrlForm.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web.dtos; 2 | 3 | 4 | import jakarta.validation.constraints.Max; 5 | import jakarta.validation.constraints.Min; 6 | import jakarta.validation.constraints.NotBlank; 7 | 8 | public record CreateShortUrlForm( 9 | @NotBlank(message = "Original URL is required") 10 | String originalUrl, 11 | Boolean isPrivate, 12 | @Min(1) 13 | @Max(365) 14 | Integer expirationInDays 15 | ) { 16 | } 17 | -------------------------------------------------------------------------------- /part-12/src/main/resources/templates/error/500.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | Server Error 10 | 11 | 12 |
13 |

Some Error Occurred While Processing Your Request

14 |
15 | 16 | -------------------------------------------------------------------------------- /part-6/src/main/resources/templates/error/500.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | Server Error 10 | 11 | 12 |
13 |

Some Error Occurred While Processing Your Request

14 |
15 | 16 | -------------------------------------------------------------------------------- /part-7/src/main/resources/templates/error/500.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | Server Error 10 | 11 | 12 |
13 |

Some Error Occurred While Processing Your Request

14 |
15 | 16 | -------------------------------------------------------------------------------- /part-8/src/main/java/com/sivalabs/urlshortener/web/dtos/CreateShortUrlForm.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web.dtos; 2 | 3 | 4 | import jakarta.validation.constraints.Max; 5 | import jakarta.validation.constraints.Min; 6 | import jakarta.validation.constraints.NotBlank; 7 | 8 | public record CreateShortUrlForm( 9 | @NotBlank(message = "Original URL is required") 10 | String originalUrl, 11 | Boolean isPrivate, 12 | @Min(1) 13 | @Max(365) 14 | Integer expirationInDays 15 | ) { 16 | } 17 | -------------------------------------------------------------------------------- /part-8/src/main/resources/templates/error/500.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | Server Error 10 | 11 | 12 |
13 |

Some Error Occurred While Processing Your Request

14 |
15 | 16 | -------------------------------------------------------------------------------- /part-9/src/main/java/com/sivalabs/urlshortener/web/dtos/CreateShortUrlForm.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web.dtos; 2 | 3 | 4 | import jakarta.validation.constraints.Max; 5 | import jakarta.validation.constraints.Min; 6 | import jakarta.validation.constraints.NotBlank; 7 | 8 | public record CreateShortUrlForm( 9 | @NotBlank(message = "Original URL is required") 10 | String originalUrl, 11 | Boolean isPrivate, 12 | @Min(1) 13 | @Max(365) 14 | Integer expirationInDays 15 | ) { 16 | } 17 | -------------------------------------------------------------------------------- /part-9/src/main/resources/templates/error/500.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | Server Error 10 | 11 | 12 |
13 |

Some Error Occurred While Processing Your Request

14 |
15 | 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /part-5/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=spring-boot-url-shortener 2 | 3 | ## App Config 4 | app.base-url=http://localhost:8080 5 | app.default-expiry-in-days=30 6 | app.validate-original-url=true 7 | 8 | spring.docker.compose.lifecycle-management=start_only 9 | 10 | spring.datasource.url=jdbc:postgresql://localhost:5432/postgres 11 | spring.datasource.username=postgres 12 | spring.datasource.password=postgres 13 | spring.jpa.show-sql=true 14 | #spring.jpa.hibernate.ddl-auto=update 15 | spring.jpa.open-in-view=false 16 | -------------------------------------------------------------------------------- /part-6/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=spring-boot-url-shortener 2 | 3 | ## App Config 4 | app.base-url=http://localhost:8080 5 | app.default-expiry-in-days=30 6 | app.validate-original-url=true 7 | 8 | spring.docker.compose.lifecycle-management=start_only 9 | 10 | spring.datasource.url=jdbc:postgresql://localhost:5432/postgres 11 | spring.datasource.username=postgres 12 | spring.datasource.password=postgres 13 | spring.jpa.show-sql=true 14 | #spring.jpa.hibernate.ddl-auto=update 15 | spring.jpa.open-in-view=false 16 | -------------------------------------------------------------------------------- /part-7/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=spring-boot-url-shortener 2 | 3 | ## App Config 4 | app.base-url=http://localhost:8080 5 | app.default-expiry-in-days=30 6 | app.validate-original-url=true 7 | 8 | spring.docker.compose.lifecycle-management=start_only 9 | 10 | spring.datasource.url=jdbc:postgresql://localhost:5432/postgres 11 | spring.datasource.username=postgres 12 | spring.datasource.password=postgres 13 | spring.jpa.show-sql=true 14 | #spring.jpa.hibernate.ddl-auto=update 15 | spring.jpa.open-in-view=false 16 | -------------------------------------------------------------------------------- /part-8/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=spring-boot-url-shortener 2 | 3 | ## App Config 4 | app.base-url=http://localhost:8080 5 | app.default-expiry-in-days=30 6 | app.validate-original-url=true 7 | 8 | spring.docker.compose.lifecycle-management=start_only 9 | 10 | spring.datasource.url=jdbc:postgresql://localhost:5432/postgres 11 | spring.datasource.username=postgres 12 | spring.datasource.password=postgres 13 | spring.jpa.show-sql=true 14 | #spring.jpa.hibernate.ddl-auto=update 15 | spring.jpa.open-in-view=false 16 | -------------------------------------------------------------------------------- /part-2/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /part-3/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /part-4/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /part-5/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /part-6/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /part-7/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /part-8/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /part-9/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /part-10/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /part-11/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /part-12/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/main/resources/templates/error/404.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | Page Not Found 10 | 11 | 12 |
13 |

Request Short URL Not Found or Expired

14 |
15 | 16 | -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/test/java/com/sivalabs/urlshortener/PasswordUtility.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 4 | import org.springframework.security.crypto.password.PasswordEncoder; 5 | 6 | public class PasswordUtility { 7 | public static void main(String[] args) { 8 | PasswordEncoder encoder = new BCryptPasswordEncoder(); 9 | System.out.println(encoder.encode("secret")); 10 | System.out.println(encoder.encode("admin")); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /part-10/src/main/java/com/sivalabs/urlshortener/domain/models/ShortUrlDto.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | import java.io.Serializable; 4 | import java.time.Instant; 5 | 6 | /** 7 | * DTO for {@link com.sivalabs.urlshortener.domain.entities.ShortUrl} 8 | */ 9 | public record ShortUrlDto(Long id, String shortKey, String originalUrl, 10 | Boolean isPrivate, Instant expiresAt, 11 | UserDto createdBy, Long clickCount, 12 | Instant createdAt) implements Serializable { 13 | } -------------------------------------------------------------------------------- /part-10/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=spring-boot-url-shortener 2 | 3 | ## App Config 4 | app.base-url=http://localhost:8080 5 | app.default-expiry-in-days=30 6 | app.validate-original-url=true 7 | app.page-size=10 8 | 9 | spring.docker.compose.lifecycle-management=start_only 10 | 11 | spring.datasource.url=jdbc:postgresql://localhost:5432/postgres 12 | spring.datasource.username=postgres 13 | spring.datasource.password=postgres 14 | spring.jpa.show-sql=true 15 | #spring.jpa.hibernate.ddl-auto=update 16 | spring.jpa.open-in-view=false 17 | -------------------------------------------------------------------------------- /part-11/src/main/java/com/sivalabs/urlshortener/domain/models/ShortUrlDto.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | import java.io.Serializable; 4 | import java.time.Instant; 5 | 6 | /** 7 | * DTO for {@link com.sivalabs.urlshortener.domain.entities.ShortUrl} 8 | */ 9 | public record ShortUrlDto(Long id, String shortKey, String originalUrl, 10 | Boolean isPrivate, Instant expiresAt, 11 | UserDto createdBy, Long clickCount, 12 | Instant createdAt) implements Serializable { 13 | } -------------------------------------------------------------------------------- /part-11/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=spring-boot-url-shortener 2 | 3 | ## App Config 4 | app.base-url=http://localhost:8080 5 | app.default-expiry-in-days=30 6 | app.validate-original-url=true 7 | app.page-size=10 8 | 9 | spring.docker.compose.lifecycle-management=start_only 10 | 11 | spring.datasource.url=jdbc:postgresql://localhost:5432/postgres 12 | spring.datasource.username=postgres 13 | spring.datasource.password=postgres 14 | spring.jpa.show-sql=true 15 | #spring.jpa.hibernate.ddl-auto=update 16 | spring.jpa.open-in-view=false 17 | -------------------------------------------------------------------------------- /part-12/src/main/java/com/sivalabs/urlshortener/domain/models/ShortUrlDto.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | import java.io.Serializable; 4 | import java.time.Instant; 5 | 6 | /** 7 | * DTO for {@link com.sivalabs.urlshortener.domain.entities.ShortUrl} 8 | */ 9 | public record ShortUrlDto(Long id, String shortKey, String originalUrl, 10 | Boolean isPrivate, Instant expiresAt, 11 | UserDto createdBy, Long clickCount, 12 | Instant createdAt) implements Serializable { 13 | } -------------------------------------------------------------------------------- /part-12/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=spring-boot-url-shortener 2 | 3 | ## App Config 4 | app.base-url=http://localhost:8080 5 | app.default-expiry-in-days=30 6 | app.validate-original-url=true 7 | app.page-size=10 8 | 9 | spring.docker.compose.lifecycle-management=start_only 10 | 11 | spring.datasource.url=jdbc:postgresql://localhost:5432/postgres 12 | spring.datasource.username=postgres 13 | spring.datasource.password=postgres 14 | spring.jpa.show-sql=true 15 | #spring.jpa.hibernate.ddl-auto=update 16 | spring.jpa.open-in-view=false 17 | -------------------------------------------------------------------------------- /part-4/src/main/java/com/sivalabs/urlshortener/domain/models/ShortUrlDto.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | import java.io.Serializable; 4 | import java.time.Instant; 5 | 6 | /** 7 | * DTO for {@link com.sivalabs.urlshortener.domain.entities.ShortUrl} 8 | */ 9 | public record ShortUrlDto(Long id, String shortKey, String originalUrl, 10 | Boolean isPrivate, Instant expiresAt, 11 | UserDto createdBy, Long clickCount, 12 | Instant createdAt) implements Serializable { 13 | } -------------------------------------------------------------------------------- /part-5/src/main/java/com/sivalabs/urlshortener/domain/models/ShortUrlDto.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | import java.io.Serializable; 4 | import java.time.Instant; 5 | 6 | /** 7 | * DTO for {@link com.sivalabs.urlshortener.domain.entities.ShortUrl} 8 | */ 9 | public record ShortUrlDto(Long id, String shortKey, String originalUrl, 10 | Boolean isPrivate, Instant expiresAt, 11 | UserDto createdBy, Long clickCount, 12 | Instant createdAt) implements Serializable { 13 | } -------------------------------------------------------------------------------- /part-6/src/main/java/com/sivalabs/urlshortener/domain/models/ShortUrlDto.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | import java.io.Serializable; 4 | import java.time.Instant; 5 | 6 | /** 7 | * DTO for {@link com.sivalabs.urlshortener.domain.entities.ShortUrl} 8 | */ 9 | public record ShortUrlDto(Long id, String shortKey, String originalUrl, 10 | Boolean isPrivate, Instant expiresAt, 11 | UserDto createdBy, Long clickCount, 12 | Instant createdAt) implements Serializable { 13 | } -------------------------------------------------------------------------------- /part-7/src/main/java/com/sivalabs/urlshortener/domain/models/ShortUrlDto.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | import java.io.Serializable; 4 | import java.time.Instant; 5 | 6 | /** 7 | * DTO for {@link com.sivalabs.urlshortener.domain.entities.ShortUrl} 8 | */ 9 | public record ShortUrlDto(Long id, String shortKey, String originalUrl, 10 | Boolean isPrivate, Instant expiresAt, 11 | UserDto createdBy, Long clickCount, 12 | Instant createdAt) implements Serializable { 13 | } -------------------------------------------------------------------------------- /part-8/src/main/java/com/sivalabs/urlshortener/domain/models/ShortUrlDto.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | import java.io.Serializable; 4 | import java.time.Instant; 5 | 6 | /** 7 | * DTO for {@link com.sivalabs.urlshortener.domain.entities.ShortUrl} 8 | */ 9 | public record ShortUrlDto(Long id, String shortKey, String originalUrl, 10 | Boolean isPrivate, Instant expiresAt, 11 | UserDto createdBy, Long clickCount, 12 | Instant createdAt) implements Serializable { 13 | } -------------------------------------------------------------------------------- /part-9/src/main/java/com/sivalabs/urlshortener/domain/models/ShortUrlDto.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | import java.io.Serializable; 4 | import java.time.Instant; 5 | 6 | /** 7 | * DTO for {@link com.sivalabs.urlshortener.domain.entities.ShortUrl} 8 | */ 9 | public record ShortUrlDto(Long id, String shortKey, String originalUrl, 10 | Boolean isPrivate, Instant expiresAt, 11 | UserDto createdBy, Long clickCount, 12 | Instant createdAt) implements Serializable { 13 | } -------------------------------------------------------------------------------- /part-9/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=spring-boot-url-shortener 2 | 3 | ## App Config 4 | app.base-url=http://localhost:8080 5 | app.default-expiry-in-days=30 6 | app.validate-original-url=true 7 | app.page-size=10 8 | 9 | spring.docker.compose.lifecycle-management=start_only 10 | 11 | spring.datasource.url=jdbc:postgresql://localhost:5432/postgres 12 | spring.datasource.username=postgres 13 | spring.datasource.password=postgres 14 | spring.jpa.show-sql=true 15 | #spring.jpa.hibernate.ddl-auto=update 16 | spring.jpa.open-in-view=false 17 | -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/main/java/com/sivalabs/urlshortener/web/dtos/CreateShortUrlForm.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web.dtos; 2 | 3 | 4 | import jakarta.validation.constraints.Max; 5 | import jakarta.validation.constraints.Min; 6 | import jakarta.validation.constraints.NotBlank; 7 | 8 | public record CreateShortUrlForm( 9 | @NotBlank(message = "Original URL is required") 10 | String originalUrl, 11 | Boolean isPrivate, 12 | @Min(1) 13 | @Max(365) 14 | Integer expirationInDays 15 | ) { 16 | } 17 | -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/main/resources/templates/error/500.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | Server Error 10 | 11 | 12 |
13 |

Some Error Occurred While Processing Your Request

14 |
15 | 16 | -------------------------------------------------------------------------------- /part-11/src/main/java/com/sivalabs/urlshortener/web/dtos/RegisterUserRequest.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web.dtos; 2 | 3 | import jakarta.validation.constraints.Email; 4 | import jakarta.validation.constraints.NotBlank; 5 | 6 | public record RegisterUserRequest( 7 | @NotBlank(message = "Email is required") 8 | @Email(message = "Invalid email format") 9 | String email, 10 | @NotBlank(message = "Password is required") 11 | String password, 12 | @NotBlank(message = "Name is required") 13 | String name 14 | ) { 15 | } -------------------------------------------------------------------------------- /part-12/src/main/java/com/sivalabs/urlshortener/web/dtos/RegisterUserRequest.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web.dtos; 2 | 3 | import jakarta.validation.constraints.Email; 4 | import jakarta.validation.constraints.NotBlank; 5 | 6 | public record RegisterUserRequest( 7 | @NotBlank(message = "Email is required") 8 | @Email(message = "Invalid email format") 9 | String email, 10 | @NotBlank(message = "Password is required") 11 | String password, 12 | @NotBlank(message = "Name is required") 13 | String name 14 | ) { 15 | } -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/main/java/com/sivalabs/urlshortener/domain/models/ShortUrlDto.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | import java.io.Serializable; 4 | import java.time.Instant; 5 | 6 | /** 7 | * DTO for {@link com.sivalabs.urlshortener.domain.entities.ShortUrl} 8 | */ 9 | public record ShortUrlDto(Long id, String shortKey, String originalUrl, 10 | Boolean isPrivate, Instant expiresAt, 11 | UserDto createdBy, Long clickCount, 12 | Instant createdAt) implements Serializable { 13 | } -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=spring-boot-url-shortener 2 | 3 | ## App Config 4 | app.base-url=http://localhost:8080 5 | app.default-expiry-in-days=30 6 | app.validate-original-url=true 7 | app.page-size=10 8 | 9 | spring.docker.compose.lifecycle-management=start_only 10 | 11 | spring.datasource.url=jdbc:postgresql://localhost:5432/postgres 12 | spring.datasource.username=postgres 13 | spring.datasource.password=postgres 14 | spring.jpa.show-sql=true 15 | #spring.jpa.hibernate.ddl-auto=update 16 | spring.jpa.open-in-view=false 17 | -------------------------------------------------------------------------------- /part-10/src/main/java/com/sivalabs/urlshortener/SpringBootUrlShortenerApplication.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.context.properties.ConfigurationPropertiesScan; 6 | 7 | @SpringBootApplication 8 | @ConfigurationPropertiesScan 9 | public class SpringBootUrlShortenerApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(SpringBootUrlShortenerApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /part-11/src/main/java/com/sivalabs/urlshortener/SpringBootUrlShortenerApplication.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.context.properties.ConfigurationPropertiesScan; 6 | 7 | @SpringBootApplication 8 | @ConfigurationPropertiesScan 9 | public class SpringBootUrlShortenerApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(SpringBootUrlShortenerApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /part-12/src/main/java/com/sivalabs/urlshortener/SpringBootUrlShortenerApplication.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.context.properties.ConfigurationPropertiesScan; 6 | 7 | @SpringBootApplication 8 | @ConfigurationPropertiesScan 9 | public class SpringBootUrlShortenerApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(SpringBootUrlShortenerApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /part-3/src/main/java/com/sivalabs/urlshortener/domain/repositories/ShortUrlRepository.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.repositories; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.ShortUrl; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.jpa.repository.Query; 6 | 7 | import java.util.List; 8 | 9 | public interface ShortUrlRepository extends JpaRepository { 10 | @Query("select su from ShortUrl su where su.isPrivate = false order by su.createdAt desc") 11 | List findPublicShortUrls(); 12 | } 13 | -------------------------------------------------------------------------------- /part-5/src/main/java/com/sivalabs/urlshortener/SpringBootUrlShortenerApplication.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.context.properties.ConfigurationPropertiesScan; 6 | 7 | @SpringBootApplication 8 | @ConfigurationPropertiesScan 9 | public class SpringBootUrlShortenerApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(SpringBootUrlShortenerApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /part-6/src/main/java/com/sivalabs/urlshortener/SpringBootUrlShortenerApplication.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.context.properties.ConfigurationPropertiesScan; 6 | 7 | @SpringBootApplication 8 | @ConfigurationPropertiesScan 9 | public class SpringBootUrlShortenerApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(SpringBootUrlShortenerApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /part-7/src/main/java/com/sivalabs/urlshortener/SpringBootUrlShortenerApplication.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.context.properties.ConfigurationPropertiesScan; 6 | 7 | @SpringBootApplication 8 | @ConfigurationPropertiesScan 9 | public class SpringBootUrlShortenerApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(SpringBootUrlShortenerApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /part-8/src/main/java/com/sivalabs/urlshortener/SpringBootUrlShortenerApplication.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.context.properties.ConfigurationPropertiesScan; 6 | 7 | @SpringBootApplication 8 | @ConfigurationPropertiesScan 9 | public class SpringBootUrlShortenerApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(SpringBootUrlShortenerApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /part-9/src/main/java/com/sivalabs/urlshortener/SpringBootUrlShortenerApplication.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.context.properties.ConfigurationPropertiesScan; 6 | 7 | @SpringBootApplication 8 | @ConfigurationPropertiesScan 9 | public class SpringBootUrlShortenerApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(SpringBootUrlShortenerApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/main/java/com/sivalabs/urlshortener/web/dtos/RegisterUserRequest.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web.dtos; 2 | 3 | import jakarta.validation.constraints.Email; 4 | import jakarta.validation.constraints.NotBlank; 5 | 6 | public record RegisterUserRequest( 7 | @NotBlank(message = "Email is required") 8 | @Email(message = "Invalid email format") 9 | String email, 10 | @NotBlank(message = "Password is required") 11 | String password, 12 | @NotBlank(message = "Name is required") 13 | String name 14 | ) { 15 | } -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/main/java/com/sivalabs/urlshortener/SpringBootUrlShortenerApplication.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.context.properties.ConfigurationPropertiesScan; 6 | 7 | @SpringBootApplication 8 | @ConfigurationPropertiesScan 9 | public class SpringBootUrlShortenerApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(SpringBootUrlShortenerApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /part-4/src/main/java/com/sivalabs/urlshortener/domain/repositories/ShortUrlRepository.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.repositories; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.ShortUrl; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.jpa.repository.Query; 6 | 7 | import java.util.List; 8 | 9 | public interface ShortUrlRepository extends JpaRepository { 10 | @Query("select su from ShortUrl su left join fetch su.createdBy where su.isPrivate = false order by su.createdAt desc") 11 | List findPublicShortUrls(); 12 | } 13 | -------------------------------------------------------------------------------- /part-2/src/main/resources/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | Home 10 | 11 | 12 |
13 |
14 |

A simple URL shortener service. Create short, easy-to-share links.

15 |
16 |
17 | 18 | -------------------------------------------------------------------------------- /part-2/src/main/java/com/sivalabs/urlshortener/HomeController.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web.controllers; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.ui.Model; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | 7 | import java.util.List; 8 | 9 | @Controller 10 | public class HomeController { 11 | 12 | @GetMapping("/") 13 | public String home(Model model) { 14 | model.addAttribute("title", "URL Shortener - Thymeleaf"); 15 | return "index"; 16 | } 17 | 18 | 19 | @GetMapping("/about") 20 | public String about() { 21 | return "about"; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /part-5/src/main/java/com/sivalabs/urlshortener/domain/repositories/ShortUrlRepository.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.repositories; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.ShortUrl; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.jpa.repository.Query; 6 | 7 | import java.util.List; 8 | 9 | public interface ShortUrlRepository extends JpaRepository { 10 | @Query("select su from ShortUrl su left join fetch su.createdBy where su.isPrivate = false order by su.createdAt desc") 11 | List findPublicShortUrls(); 12 | 13 | boolean existsByShortKey(String shortKey); 14 | } 15 | -------------------------------------------------------------------------------- /part-12/docker/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | postgres: 3 | image: 'postgres:17' 4 | environment: 5 | - 'POSTGRES_DB=postgres' 6 | - 'POSTGRES_PASSWORD=postgres' 7 | - 'POSTGRES_USER=postgres' 8 | ports: 9 | - '5432' 10 | 11 | spring-boot-url-shortener: 12 | image: sivaprasadreddy/spring-boot-url-shortener 13 | container_name: spring-boot-url-shortener 14 | environment: 15 | SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/postgres 16 | SPRING_DATASOURCE_USERNAME: postgres 17 | SPRING_DATASOURCE_PASSWORD: postgres 18 | ports: 19 | - "8080:8080" 20 | restart: unless-stopped 21 | depends_on: 22 | - postgres 23 | 24 | -------------------------------------------------------------------------------- /part-12/src/test/java/com/sivalabs/urlshortener/TestcontainersConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.springframework.boot.test.context.TestConfiguration; 4 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection; 5 | import org.springframework.context.annotation.Bean; 6 | import org.testcontainers.containers.PostgreSQLContainer; 7 | import org.testcontainers.utility.DockerImageName; 8 | 9 | @TestConfiguration(proxyBeanMethods = false) 10 | class TestcontainersConfiguration { 11 | 12 | @Bean 13 | @ServiceConnection 14 | PostgreSQLContainer postgresContainer() { 15 | return new PostgreSQLContainer<>(DockerImageName.parse("postgres:17")); 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/docker/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | postgres: 3 | image: 'postgres:17' 4 | environment: 5 | - 'POSTGRES_DB=postgres' 6 | - 'POSTGRES_PASSWORD=postgres' 7 | - 'POSTGRES_USER=postgres' 8 | ports: 9 | - '5432' 10 | 11 | spring-boot-url-shortener: 12 | image: sivaprasadreddy/spring-boot-url-shortener 13 | container_name: spring-boot-url-shortener 14 | environment: 15 | SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/postgres 16 | SPRING_DATASOURCE_USERNAME: postgres 17 | SPRING_DATASOURCE_PASSWORD: postgres 18 | ports: 19 | - "8080:8080" 20 | restart: unless-stopped 21 | depends_on: 22 | - postgres 23 | 24 | -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/test/java/com/sivalabs/urlshortener/TestcontainersConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import org.springframework.boot.test.context.TestConfiguration; 4 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection; 5 | import org.springframework.context.annotation.Bean; 6 | import org.testcontainers.containers.PostgreSQLContainer; 7 | import org.testcontainers.utility.DockerImageName; 8 | 9 | @TestConfiguration(proxyBeanMethods = false) 10 | class TestcontainersConfiguration { 11 | 12 | @Bean 13 | @ServiceConnection 14 | PostgreSQLContainer postgresContainer() { 15 | return new PostgreSQLContainer<>(DockerImageName.parse("postgres:17")); 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /part-3/src/main/java/com/sivalabs/urlshortener/domain/services/ShortUrlService.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.ShortUrl; 4 | import com.sivalabs.urlshortener.domain.repositories.ShortUrlRepository; 5 | import org.springframework.stereotype.Service; 6 | 7 | import java.util.List; 8 | 9 | @Service 10 | public class ShortUrlService { 11 | private final ShortUrlRepository shortUrlRepository; 12 | 13 | public ShortUrlService(ShortUrlRepository shortUrlRepository) { 14 | this.shortUrlRepository = shortUrlRepository; 15 | } 16 | 17 | public List findAllPublicShortUrls() { 18 | return shortUrlRepository.findPublicShortUrls(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /part-6/src/main/java/com/sivalabs/urlshortener/domain/repositories/ShortUrlRepository.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.repositories; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.ShortUrl; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.jpa.repository.Query; 6 | 7 | import java.util.List; 8 | import java.util.Optional; 9 | 10 | public interface ShortUrlRepository extends JpaRepository { 11 | @Query("select su from ShortUrl su left join fetch su.createdBy where su.isPrivate = false order by su.createdAt desc") 12 | List findPublicShortUrls(); 13 | 14 | boolean existsByShortKey(String shortKey); 15 | 16 | Optional findByShortKey(String shortKey); 17 | } 18 | -------------------------------------------------------------------------------- /part-7/src/main/java/com/sivalabs/urlshortener/domain/repositories/ShortUrlRepository.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.repositories; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.ShortUrl; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.jpa.repository.Query; 6 | 7 | import java.util.List; 8 | import java.util.Optional; 9 | 10 | public interface ShortUrlRepository extends JpaRepository { 11 | @Query("select su from ShortUrl su left join fetch su.createdBy where su.isPrivate = false order by su.createdAt desc") 12 | List findPublicShortUrls(); 13 | 14 | boolean existsByShortKey(String shortKey); 15 | 16 | Optional findByShortKey(String shortKey); 17 | } 18 | -------------------------------------------------------------------------------- /part-8/src/main/java/com/sivalabs/urlshortener/domain/repositories/ShortUrlRepository.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.repositories; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.ShortUrl; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.jpa.repository.Query; 6 | 7 | import java.util.List; 8 | import java.util.Optional; 9 | 10 | public interface ShortUrlRepository extends JpaRepository { 11 | @Query("select su from ShortUrl su left join fetch su.createdBy where su.isPrivate = false order by su.createdAt desc") 12 | List findPublicShortUrls(); 13 | 14 | boolean existsByShortKey(String shortKey); 15 | 16 | Optional findByShortKey(String shortKey); 17 | } 18 | -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot URL Shortener 2 | 3 | URL Shortener is a service that shortens a long URL into a short URL which is easier to share. 4 | 5 | ## Prerequisites 6 | * Java 21 7 | * Docker 8 | 9 | ## Tech Stack 10 | * Spring Boot 11 | * Spring Security 12 | * Spring Data JPA 13 | * PostgreSQL 14 | * FlywayDb Migrations 15 | * Thymeleaf 16 | * Bootstrap CSS 17 | 18 | ```shell 19 | # To build Docker image with default name sivaprasadreddy/spring-boot-url-shortener 20 | $ ./mvnw spring-boot:build-image 21 | 22 | # To build Docker image with custom name 23 | $ ./mvnw spring-boot:build-image -DdockerImageName=sivalabs/spring-boot-url-shortener 24 | 25 | # To run the application using Docker Compose 26 | $ cd docker 27 | $ docker compose up -d 28 | ``` -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/main/java/com/sivalabs/urlshortener/domain/services/RandomUtils.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import java.security.SecureRandom; 4 | 5 | public class RandomUtils { 6 | private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 7 | private static final int SHORT_KEY_LENGTH = 6; 8 | private static final SecureRandom RANDOM = new SecureRandom(); 9 | 10 | public static String generateRandomShortKey() { 11 | StringBuilder sb = new StringBuilder(SHORT_KEY_LENGTH); 12 | for (int i = 0; i < SHORT_KEY_LENGTH; i++) { 13 | sb.append(CHARACTERS.charAt(RANDOM.nextInt(CHARACTERS.length()))); 14 | } 15 | return sb.toString(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /part-9/src/main/java/com/sivalabs/urlshortener/domain/repositories/ShortUrlRepository.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.repositories; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.ShortUrl; 4 | import org.springframework.data.domain.Page; 5 | import org.springframework.data.domain.Pageable; 6 | import org.springframework.data.jpa.repository.JpaRepository; 7 | import org.springframework.data.jpa.repository.Query; 8 | 9 | import java.util.List; 10 | import java.util.Optional; 11 | 12 | public interface ShortUrlRepository extends JpaRepository { 13 | @Query("select su from ShortUrl su left join fetch su.createdBy where su.isPrivate = false") 14 | Page findPublicShortUrls(Pageable pageable); 15 | 16 | boolean existsByShortKey(String shortKey); 17 | 18 | Optional findByShortKey(String shortKey); 19 | } 20 | -------------------------------------------------------------------------------- /part-5/src/main/java/com/sivalabs/urlshortener/ApplicationProperties.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import jakarta.validation.constraints.Max; 4 | import jakarta.validation.constraints.Min; 5 | import jakarta.validation.constraints.NotBlank; 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | import org.springframework.boot.context.properties.bind.DefaultValue; 8 | import org.springframework.validation.annotation.Validated; 9 | 10 | @ConfigurationProperties(prefix = "app") 11 | @Validated 12 | public record ApplicationProperties( 13 | @NotBlank 14 | @DefaultValue("http://localhost:8080") 15 | String baseUrl, 16 | @DefaultValue("30") 17 | @Min(1) 18 | @Max(365) 19 | int defaultExpiryInDays, 20 | @DefaultValue("true") 21 | boolean validateOriginalUrl 22 | ) { 23 | } 24 | -------------------------------------------------------------------------------- /part-6/src/main/java/com/sivalabs/urlshortener/ApplicationProperties.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import jakarta.validation.constraints.Max; 4 | import jakarta.validation.constraints.Min; 5 | import jakarta.validation.constraints.NotBlank; 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | import org.springframework.boot.context.properties.bind.DefaultValue; 8 | import org.springframework.validation.annotation.Validated; 9 | 10 | @ConfigurationProperties(prefix = "app") 11 | @Validated 12 | public record ApplicationProperties( 13 | @NotBlank 14 | @DefaultValue("http://localhost:8080") 15 | String baseUrl, 16 | @DefaultValue("30") 17 | @Min(1) 18 | @Max(365) 19 | int defaultExpiryInDays, 20 | @DefaultValue("true") 21 | boolean validateOriginalUrl 22 | ) { 23 | } 24 | -------------------------------------------------------------------------------- /part-7/src/main/java/com/sivalabs/urlshortener/ApplicationProperties.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import jakarta.validation.constraints.Max; 4 | import jakarta.validation.constraints.Min; 5 | import jakarta.validation.constraints.NotBlank; 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | import org.springframework.boot.context.properties.bind.DefaultValue; 8 | import org.springframework.validation.annotation.Validated; 9 | 10 | @ConfigurationProperties(prefix = "app") 11 | @Validated 12 | public record ApplicationProperties( 13 | @NotBlank 14 | @DefaultValue("http://localhost:8080") 15 | String baseUrl, 16 | @DefaultValue("30") 17 | @Min(1) 18 | @Max(365) 19 | int defaultExpiryInDays, 20 | @DefaultValue("true") 21 | boolean validateOriginalUrl 22 | ) { 23 | } 24 | -------------------------------------------------------------------------------- /part-8/src/main/java/com/sivalabs/urlshortener/ApplicationProperties.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import jakarta.validation.constraints.Max; 4 | import jakarta.validation.constraints.Min; 5 | import jakarta.validation.constraints.NotBlank; 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | import org.springframework.boot.context.properties.bind.DefaultValue; 8 | import org.springframework.validation.annotation.Validated; 9 | 10 | @ConfigurationProperties(prefix = "app") 11 | @Validated 12 | public record ApplicationProperties( 13 | @NotBlank 14 | @DefaultValue("http://localhost:8080") 15 | String baseUrl, 16 | @DefaultValue("30") 17 | @Min(1) 18 | @Max(365) 19 | int defaultExpiryInDays, 20 | @DefaultValue("true") 21 | boolean validateOriginalUrl 22 | ) { 23 | } 24 | -------------------------------------------------------------------------------- /part-3/src/main/resources/db/migration/V1__create_tables.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE users 2 | ( 3 | id BIGSERIAL PRIMARY KEY, 4 | email VARCHAR(100) NOT NULL UNIQUE, 5 | password VARCHAR(100) NOT NULL, 6 | name VARCHAR(100) NOT NULL, 7 | role VARCHAR(20) NOT NULL DEFAULT 'ROLE_USER', 8 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP 9 | ); 10 | 11 | CREATE TABLE short_urls 12 | ( 13 | id BIGSERIAL PRIMARY KEY, 14 | short_key VARCHAR(10) NOT NULL UNIQUE, 15 | original_url TEXT NOT NULL, 16 | is_private BOOLEAN NOT NULL DEFAULT FALSE, 17 | expires_at TIMESTAMP, 18 | created_by BIGINT, 19 | click_count BIGINT NOT NULL DEFAULT 0, 20 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 21 | CONSTRAINT fk_short_urls_users FOREIGN KEY (created_by) REFERENCES users (id) 22 | ); 23 | 24 | -------------------------------------------------------------------------------- /part-4/src/main/resources/db/migration/V1__create_tables.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE users 2 | ( 3 | id BIGSERIAL PRIMARY KEY, 4 | email VARCHAR(100) NOT NULL UNIQUE, 5 | password VARCHAR(100) NOT NULL, 6 | name VARCHAR(100) NOT NULL, 7 | role VARCHAR(20) NOT NULL DEFAULT 'ROLE_USER', 8 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP 9 | ); 10 | 11 | CREATE TABLE short_urls 12 | ( 13 | id BIGSERIAL PRIMARY KEY, 14 | short_key VARCHAR(10) NOT NULL UNIQUE, 15 | original_url TEXT NOT NULL, 16 | is_private BOOLEAN NOT NULL DEFAULT FALSE, 17 | expires_at TIMESTAMP, 18 | created_by BIGINT, 19 | click_count BIGINT NOT NULL DEFAULT 0, 20 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 21 | CONSTRAINT fk_short_urls_users FOREIGN KEY (created_by) REFERENCES users (id) 22 | ); 23 | 24 | -------------------------------------------------------------------------------- /part-5/src/main/resources/db/migration/V1__create_tables.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE users 2 | ( 3 | id BIGSERIAL PRIMARY KEY, 4 | email VARCHAR(100) NOT NULL UNIQUE, 5 | password VARCHAR(100) NOT NULL, 6 | name VARCHAR(100) NOT NULL, 7 | role VARCHAR(20) NOT NULL DEFAULT 'ROLE_USER', 8 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP 9 | ); 10 | 11 | CREATE TABLE short_urls 12 | ( 13 | id BIGSERIAL PRIMARY KEY, 14 | short_key VARCHAR(10) NOT NULL UNIQUE, 15 | original_url TEXT NOT NULL, 16 | is_private BOOLEAN NOT NULL DEFAULT FALSE, 17 | expires_at TIMESTAMP, 18 | created_by BIGINT, 19 | click_count BIGINT NOT NULL DEFAULT 0, 20 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 21 | CONSTRAINT fk_short_urls_users FOREIGN KEY (created_by) REFERENCES users (id) 22 | ); 23 | 24 | -------------------------------------------------------------------------------- /part-6/src/main/resources/db/migration/V1__create_tables.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE users 2 | ( 3 | id BIGSERIAL PRIMARY KEY, 4 | email VARCHAR(100) NOT NULL UNIQUE, 5 | password VARCHAR(100) NOT NULL, 6 | name VARCHAR(100) NOT NULL, 7 | role VARCHAR(20) NOT NULL DEFAULT 'ROLE_USER', 8 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP 9 | ); 10 | 11 | CREATE TABLE short_urls 12 | ( 13 | id BIGSERIAL PRIMARY KEY, 14 | short_key VARCHAR(10) NOT NULL UNIQUE, 15 | original_url TEXT NOT NULL, 16 | is_private BOOLEAN NOT NULL DEFAULT FALSE, 17 | expires_at TIMESTAMP, 18 | created_by BIGINT, 19 | click_count BIGINT NOT NULL DEFAULT 0, 20 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 21 | CONSTRAINT fk_short_urls_users FOREIGN KEY (created_by) REFERENCES users (id) 22 | ); 23 | 24 | -------------------------------------------------------------------------------- /part-7/src/main/resources/db/migration/V1__create_tables.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE users 2 | ( 3 | id BIGSERIAL PRIMARY KEY, 4 | email VARCHAR(100) NOT NULL UNIQUE, 5 | password VARCHAR(100) NOT NULL, 6 | name VARCHAR(100) NOT NULL, 7 | role VARCHAR(20) NOT NULL DEFAULT 'ROLE_USER', 8 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP 9 | ); 10 | 11 | CREATE TABLE short_urls 12 | ( 13 | id BIGSERIAL PRIMARY KEY, 14 | short_key VARCHAR(10) NOT NULL UNIQUE, 15 | original_url TEXT NOT NULL, 16 | is_private BOOLEAN NOT NULL DEFAULT FALSE, 17 | expires_at TIMESTAMP, 18 | created_by BIGINT, 19 | click_count BIGINT NOT NULL DEFAULT 0, 20 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 21 | CONSTRAINT fk_short_urls_users FOREIGN KEY (created_by) REFERENCES users (id) 22 | ); 23 | 24 | -------------------------------------------------------------------------------- /part-8/src/main/resources/db/migration/V1__create_tables.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE users 2 | ( 3 | id BIGSERIAL PRIMARY KEY, 4 | email VARCHAR(100) NOT NULL UNIQUE, 5 | password VARCHAR(100) NOT NULL, 6 | name VARCHAR(100) NOT NULL, 7 | role VARCHAR(20) NOT NULL DEFAULT 'ROLE_USER', 8 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP 9 | ); 10 | 11 | CREATE TABLE short_urls 12 | ( 13 | id BIGSERIAL PRIMARY KEY, 14 | short_key VARCHAR(10) NOT NULL UNIQUE, 15 | original_url TEXT NOT NULL, 16 | is_private BOOLEAN NOT NULL DEFAULT FALSE, 17 | expires_at TIMESTAMP, 18 | created_by BIGINT, 19 | click_count BIGINT NOT NULL DEFAULT 0, 20 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 21 | CONSTRAINT fk_short_urls_users FOREIGN KEY (created_by) REFERENCES users (id) 22 | ); 23 | 24 | -------------------------------------------------------------------------------- /part-9/src/main/resources/db/migration/V1__create_tables.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE users 2 | ( 3 | id BIGSERIAL PRIMARY KEY, 4 | email VARCHAR(100) NOT NULL UNIQUE, 5 | password VARCHAR(100) NOT NULL, 6 | name VARCHAR(100) NOT NULL, 7 | role VARCHAR(20) NOT NULL DEFAULT 'ROLE_USER', 8 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP 9 | ); 10 | 11 | CREATE TABLE short_urls 12 | ( 13 | id BIGSERIAL PRIMARY KEY, 14 | short_key VARCHAR(10) NOT NULL UNIQUE, 15 | original_url TEXT NOT NULL, 16 | is_private BOOLEAN NOT NULL DEFAULT FALSE, 17 | expires_at TIMESTAMP, 18 | created_by BIGINT, 19 | click_count BIGINT NOT NULL DEFAULT 0, 20 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 21 | CONSTRAINT fk_short_urls_users FOREIGN KEY (created_by) REFERENCES users (id) 22 | ); 23 | 24 | -------------------------------------------------------------------------------- /part-10/src/main/resources/db/migration/V1__create_tables.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE users 2 | ( 3 | id BIGSERIAL PRIMARY KEY, 4 | email VARCHAR(100) NOT NULL UNIQUE, 5 | password VARCHAR(100) NOT NULL, 6 | name VARCHAR(100) NOT NULL, 7 | role VARCHAR(20) NOT NULL DEFAULT 'ROLE_USER', 8 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP 9 | ); 10 | 11 | CREATE TABLE short_urls 12 | ( 13 | id BIGSERIAL PRIMARY KEY, 14 | short_key VARCHAR(10) NOT NULL UNIQUE, 15 | original_url TEXT NOT NULL, 16 | is_private BOOLEAN NOT NULL DEFAULT FALSE, 17 | expires_at TIMESTAMP, 18 | created_by BIGINT, 19 | click_count BIGINT NOT NULL DEFAULT 0, 20 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 21 | CONSTRAINT fk_short_urls_users FOREIGN KEY (created_by) REFERENCES users (id) 22 | ); 23 | 24 | -------------------------------------------------------------------------------- /part-11/src/main/resources/db/migration/V1__create_tables.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE users 2 | ( 3 | id BIGSERIAL PRIMARY KEY, 4 | email VARCHAR(100) NOT NULL UNIQUE, 5 | password VARCHAR(100) NOT NULL, 6 | name VARCHAR(100) NOT NULL, 7 | role VARCHAR(20) NOT NULL DEFAULT 'ROLE_USER', 8 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP 9 | ); 10 | 11 | CREATE TABLE short_urls 12 | ( 13 | id BIGSERIAL PRIMARY KEY, 14 | short_key VARCHAR(10) NOT NULL UNIQUE, 15 | original_url TEXT NOT NULL, 16 | is_private BOOLEAN NOT NULL DEFAULT FALSE, 17 | expires_at TIMESTAMP, 18 | created_by BIGINT, 19 | click_count BIGINT NOT NULL DEFAULT 0, 20 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 21 | CONSTRAINT fk_short_urls_users FOREIGN KEY (created_by) REFERENCES users (id) 22 | ); 23 | 24 | -------------------------------------------------------------------------------- /part-12/src/main/resources/db/migration/V1__create_tables.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE users 2 | ( 3 | id BIGSERIAL PRIMARY KEY, 4 | email VARCHAR(100) NOT NULL UNIQUE, 5 | password VARCHAR(100) NOT NULL, 6 | name VARCHAR(100) NOT NULL, 7 | role VARCHAR(20) NOT NULL DEFAULT 'ROLE_USER', 8 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP 9 | ); 10 | 11 | CREATE TABLE short_urls 12 | ( 13 | id BIGSERIAL PRIMARY KEY, 14 | short_key VARCHAR(10) NOT NULL UNIQUE, 15 | original_url TEXT NOT NULL, 16 | is_private BOOLEAN NOT NULL DEFAULT FALSE, 17 | expires_at TIMESTAMP, 18 | created_by BIGINT, 19 | click_count BIGINT NOT NULL DEFAULT 0, 20 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 21 | CONSTRAINT fk_short_urls_users FOREIGN KEY (created_by) REFERENCES users (id) 22 | ); 23 | 24 | -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/main/resources/db/migration/V1__create_tables.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE users 2 | ( 3 | id BIGSERIAL PRIMARY KEY, 4 | email VARCHAR(100) NOT NULL UNIQUE, 5 | password VARCHAR(100) NOT NULL, 6 | name VARCHAR(100) NOT NULL, 7 | role VARCHAR(20) NOT NULL DEFAULT 'ROLE_USER', 8 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP 9 | ); 10 | 11 | CREATE TABLE short_urls 12 | ( 13 | id BIGSERIAL PRIMARY KEY, 14 | short_key VARCHAR(10) NOT NULL UNIQUE, 15 | original_url TEXT NOT NULL, 16 | is_private BOOLEAN NOT NULL DEFAULT FALSE, 17 | expires_at TIMESTAMP, 18 | created_by BIGINT, 19 | click_count BIGINT NOT NULL DEFAULT 0, 20 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 21 | CONSTRAINT fk_short_urls_users FOREIGN KEY (created_by) REFERENCES users (id) 22 | ); 23 | 24 | -------------------------------------------------------------------------------- /part-10/src/main/java/com/sivalabs/urlshortener/ApplicationProperties.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import jakarta.validation.constraints.Max; 4 | import jakarta.validation.constraints.Min; 5 | import jakarta.validation.constraints.NotBlank; 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | import org.springframework.boot.context.properties.bind.DefaultValue; 8 | import org.springframework.validation.annotation.Validated; 9 | 10 | @ConfigurationProperties(prefix = "app") 11 | @Validated 12 | public record ApplicationProperties( 13 | @NotBlank 14 | @DefaultValue("http://localhost:8080") 15 | String baseUrl, 16 | @DefaultValue("30") 17 | @Min(1) 18 | @Max(365) 19 | int defaultExpiryInDays, 20 | @DefaultValue("true") 21 | boolean validateOriginalUrl, 22 | @DefaultValue("10") 23 | int pageSize 24 | ) { 25 | } 26 | -------------------------------------------------------------------------------- /part-11/src/main/java/com/sivalabs/urlshortener/ApplicationProperties.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import jakarta.validation.constraints.Max; 4 | import jakarta.validation.constraints.Min; 5 | import jakarta.validation.constraints.NotBlank; 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | import org.springframework.boot.context.properties.bind.DefaultValue; 8 | import org.springframework.validation.annotation.Validated; 9 | 10 | @ConfigurationProperties(prefix = "app") 11 | @Validated 12 | public record ApplicationProperties( 13 | @NotBlank 14 | @DefaultValue("http://localhost:8080") 15 | String baseUrl, 16 | @DefaultValue("30") 17 | @Min(1) 18 | @Max(365) 19 | int defaultExpiryInDays, 20 | @DefaultValue("true") 21 | boolean validateOriginalUrl, 22 | @DefaultValue("10") 23 | int pageSize 24 | ) { 25 | } 26 | -------------------------------------------------------------------------------- /part-12/src/main/java/com/sivalabs/urlshortener/ApplicationProperties.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import jakarta.validation.constraints.Max; 4 | import jakarta.validation.constraints.Min; 5 | import jakarta.validation.constraints.NotBlank; 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | import org.springframework.boot.context.properties.bind.DefaultValue; 8 | import org.springframework.validation.annotation.Validated; 9 | 10 | @ConfigurationProperties(prefix = "app") 11 | @Validated 12 | public record ApplicationProperties( 13 | @NotBlank 14 | @DefaultValue("http://localhost:8080") 15 | String baseUrl, 16 | @DefaultValue("30") 17 | @Min(1) 18 | @Max(365) 19 | int defaultExpiryInDays, 20 | @DefaultValue("true") 21 | boolean validateOriginalUrl, 22 | @DefaultValue("10") 23 | int pageSize 24 | ) { 25 | } 26 | -------------------------------------------------------------------------------- /part-9/src/main/java/com/sivalabs/urlshortener/ApplicationProperties.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import jakarta.validation.constraints.Max; 4 | import jakarta.validation.constraints.Min; 5 | import jakarta.validation.constraints.NotBlank; 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | import org.springframework.boot.context.properties.bind.DefaultValue; 8 | import org.springframework.validation.annotation.Validated; 9 | 10 | @ConfigurationProperties(prefix = "app") 11 | @Validated 12 | public record ApplicationProperties( 13 | @NotBlank 14 | @DefaultValue("http://localhost:8080") 15 | String baseUrl, 16 | @DefaultValue("30") 17 | @Min(1) 18 | @Max(365) 19 | int defaultExpiryInDays, 20 | @DefaultValue("true") 21 | boolean validateOriginalUrl, 22 | @DefaultValue("10") 23 | int pageSize 24 | ) { 25 | } 26 | -------------------------------------------------------------------------------- /part-9/src/main/java/com/sivalabs/urlshortener/domain/models/PagedResult.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | import org.springframework.data.domain.Page; 4 | 5 | import java.util.List; 6 | 7 | public record PagedResult( 8 | List data, 9 | int pageNumber, 10 | int totalPages, 11 | long totalElements, 12 | boolean isFirst, 13 | boolean isLast, 14 | boolean hasNext, 15 | boolean hasPrevious) { 16 | 17 | public static PagedResult from(Page page) { 18 | return new PagedResult<>( 19 | page.getContent(), 20 | page.getNumber() + 1, //to show 1-based page numbering 21 | page.getTotalPages(), 22 | page.getTotalElements(), 23 | page.isFirst(), 24 | page.isLast(), 25 | page.hasNext(), 26 | page.hasPrevious()); 27 | } 28 | } -------------------------------------------------------------------------------- /part-10/src/main/java/com/sivalabs/urlshortener/domain/models/PagedResult.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | import org.springframework.data.domain.Page; 4 | 5 | import java.util.List; 6 | 7 | public record PagedResult( 8 | List data, 9 | int pageNumber, 10 | int totalPages, 11 | long totalElements, 12 | boolean isFirst, 13 | boolean isLast, 14 | boolean hasNext, 15 | boolean hasPrevious) { 16 | 17 | public static PagedResult from(Page page) { 18 | return new PagedResult<>( 19 | page.getContent(), 20 | page.getNumber() + 1, //to show 1-based page numbering 21 | page.getTotalPages(), 22 | page.getTotalElements(), 23 | page.isFirst(), 24 | page.isLast(), 25 | page.hasNext(), 26 | page.hasPrevious()); 27 | } 28 | } -------------------------------------------------------------------------------- /part-11/src/main/java/com/sivalabs/urlshortener/domain/models/PagedResult.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | import org.springframework.data.domain.Page; 4 | 5 | import java.util.List; 6 | 7 | public record PagedResult( 8 | List data, 9 | int pageNumber, 10 | int totalPages, 11 | long totalElements, 12 | boolean isFirst, 13 | boolean isLast, 14 | boolean hasNext, 15 | boolean hasPrevious) { 16 | 17 | public static PagedResult from(Page page) { 18 | return new PagedResult<>( 19 | page.getContent(), 20 | page.getNumber() + 1, //to show 1-based page numbering 21 | page.getTotalPages(), 22 | page.getTotalElements(), 23 | page.isFirst(), 24 | page.isLast(), 25 | page.hasNext(), 26 | page.hasPrevious()); 27 | } 28 | } -------------------------------------------------------------------------------- /part-12/src/main/java/com/sivalabs/urlshortener/domain/models/PagedResult.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | import org.springframework.data.domain.Page; 4 | 5 | import java.util.List; 6 | 7 | public record PagedResult( 8 | List data, 9 | int pageNumber, 10 | int totalPages, 11 | long totalElements, 12 | boolean isFirst, 13 | boolean isLast, 14 | boolean hasNext, 15 | boolean hasPrevious) { 16 | 17 | public static PagedResult from(Page page) { 18 | return new PagedResult<>( 19 | page.getContent(), 20 | page.getNumber() + 1, //to show 1-based page numbering 21 | page.getTotalPages(), 22 | page.getTotalElements(), 23 | page.isFirst(), 24 | page.isLast(), 25 | page.hasNext(), 26 | page.hasPrevious()); 27 | } 28 | } -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/main/java/com/sivalabs/urlshortener/ApplicationProperties.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener; 2 | 3 | import jakarta.validation.constraints.Max; 4 | import jakarta.validation.constraints.Min; 5 | import jakarta.validation.constraints.NotBlank; 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | import org.springframework.boot.context.properties.bind.DefaultValue; 8 | import org.springframework.validation.annotation.Validated; 9 | 10 | @ConfigurationProperties(prefix = "app") 11 | @Validated 12 | public record ApplicationProperties( 13 | @NotBlank 14 | @DefaultValue("http://localhost:8080") 15 | String baseUrl, 16 | @DefaultValue("30") 17 | @Min(1) 18 | @Max(365) 19 | int defaultExpiryInDays, 20 | @DefaultValue("true") 21 | boolean validateOriginalUrl, 22 | @DefaultValue("10") 23 | int pageSize 24 | ) { 25 | } 26 | -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/main/java/com/sivalabs/urlshortener/domain/models/PagedResult.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.models; 2 | 3 | import org.springframework.data.domain.Page; 4 | 5 | import java.util.List; 6 | 7 | public record PagedResult( 8 | List data, 9 | int pageNumber, 10 | int totalPages, 11 | long totalElements, 12 | boolean isFirst, 13 | boolean isLast, 14 | boolean hasNext, 15 | boolean hasPrevious) { 16 | 17 | public static PagedResult from(Page page) { 18 | return new PagedResult<>( 19 | page.getContent(), 20 | page.getNumber() + 1, //to show 1-based page numbering 21 | page.getTotalPages(), 22 | page.getTotalElements(), 23 | page.isFirst(), 24 | page.isLast(), 25 | page.hasNext(), 26 | page.hasPrevious()); 27 | } 28 | } -------------------------------------------------------------------------------- /part-3/src/main/java/com/sivalabs/urlshortener/web/controllers/HomeController.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web.controllers; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.ShortUrl; 4 | import com.sivalabs.urlshortener.domain.services.ShortUrlService; 5 | import org.springframework.stereotype.Controller; 6 | import org.springframework.ui.Model; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | 9 | import java.util.List; 10 | 11 | @Controller 12 | public class HomeController { 13 | private final ShortUrlService shortUrlService; 14 | 15 | public HomeController(ShortUrlService shortUrlService) { 16 | this.shortUrlService = shortUrlService; 17 | } 18 | 19 | @GetMapping("/") 20 | public String home(Model model) { 21 | List shortUrls = shortUrlService.findAllPublicShortUrls(); 22 | model.addAttribute("shortUrls", shortUrls); 23 | model.addAttribute("baseUrl", "http://localhost:8080"); 24 | return "index"; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /part-4/src/main/java/com/sivalabs/urlshortener/web/controllers/HomeController.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web.controllers; 2 | 3 | import com.sivalabs.urlshortener.domain.models.ShortUrlDto; 4 | import com.sivalabs.urlshortener.domain.services.ShortUrlService; 5 | import org.springframework.stereotype.Controller; 6 | import org.springframework.ui.Model; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | 9 | import java.util.List; 10 | 11 | @Controller 12 | public class HomeController { 13 | private final ShortUrlService shortUrlService; 14 | 15 | public HomeController(ShortUrlService shortUrlService) { 16 | this.shortUrlService = shortUrlService; 17 | } 18 | 19 | @GetMapping("/") 20 | public String home(Model model) { 21 | List shortUrls = shortUrlService.findAllPublicShortUrls(); 22 | model.addAttribute("shortUrls", shortUrls); 23 | model.addAttribute("baseUrl", "http://localhost:8080"); 24 | return "index"; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /part-6/src/main/java/com/sivalabs/urlshortener/web/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web; 2 | 3 | import com.sivalabs.urlshortener.domain.exceptions.ShortUrlNotFoundException; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.web.bind.annotation.ControllerAdvice; 7 | import org.springframework.web.bind.annotation.ExceptionHandler; 8 | 9 | @ControllerAdvice 10 | public class GlobalExceptionHandler { 11 | private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); 12 | 13 | @ExceptionHandler(ShortUrlNotFoundException.class) 14 | String handleShortUrlNotFoundException(ShortUrlNotFoundException ex) { 15 | log.error("Short URL not found: {}", ex.getMessage()); 16 | return "error/404"; 17 | } 18 | 19 | @ExceptionHandler(Exception.class) 20 | String handleException(Exception ex) { 21 | log.error("Unhandled exception: {}", ex.getMessage(), ex); 22 | return "error/500"; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /part-7/src/main/java/com/sivalabs/urlshortener/web/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web; 2 | 3 | import com.sivalabs.urlshortener.domain.exceptions.ShortUrlNotFoundException; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.web.bind.annotation.ControllerAdvice; 7 | import org.springframework.web.bind.annotation.ExceptionHandler; 8 | 9 | @ControllerAdvice 10 | public class GlobalExceptionHandler { 11 | private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); 12 | 13 | @ExceptionHandler(ShortUrlNotFoundException.class) 14 | String handleShortUrlNotFoundException(ShortUrlNotFoundException ex) { 15 | log.error("Short URL not found: {}", ex.getMessage()); 16 | return "error/404"; 17 | } 18 | 19 | @ExceptionHandler(Exception.class) 20 | String handleException(Exception ex) { 21 | log.error("Unhandled exception: {}", ex.getMessage(), ex); 22 | return "error/500"; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /part-8/src/main/java/com/sivalabs/urlshortener/web/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web; 2 | 3 | import com.sivalabs.urlshortener.domain.exceptions.ShortUrlNotFoundException; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.web.bind.annotation.ControllerAdvice; 7 | import org.springframework.web.bind.annotation.ExceptionHandler; 8 | 9 | @ControllerAdvice 10 | public class GlobalExceptionHandler { 11 | private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); 12 | 13 | @ExceptionHandler(ShortUrlNotFoundException.class) 14 | String handleShortUrlNotFoundException(ShortUrlNotFoundException ex) { 15 | log.error("Short URL not found: {}", ex.getMessage()); 16 | return "error/404"; 17 | } 18 | 19 | @ExceptionHandler(Exception.class) 20 | String handleException(Exception ex) { 21 | log.error("Unhandled exception: {}", ex.getMessage(), ex); 22 | return "error/500"; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /part-9/src/main/java/com/sivalabs/urlshortener/web/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web; 2 | 3 | import com.sivalabs.urlshortener.domain.exceptions.ShortUrlNotFoundException; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.web.bind.annotation.ControllerAdvice; 7 | import org.springframework.web.bind.annotation.ExceptionHandler; 8 | 9 | @ControllerAdvice 10 | public class GlobalExceptionHandler { 11 | private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); 12 | 13 | @ExceptionHandler(ShortUrlNotFoundException.class) 14 | String handleShortUrlNotFoundException(ShortUrlNotFoundException ex) { 15 | log.error("Short URL not found: {}", ex.getMessage()); 16 | return "error/404"; 17 | } 18 | 19 | @ExceptionHandler(Exception.class) 20 | String handleException(Exception ex) { 21 | log.error("Unhandled exception: {}", ex.getMessage(), ex); 22 | return "error/500"; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /part-10/src/main/java/com/sivalabs/urlshortener/web/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web; 2 | 3 | import com.sivalabs.urlshortener.domain.exceptions.ShortUrlNotFoundException; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.web.bind.annotation.ControllerAdvice; 7 | import org.springframework.web.bind.annotation.ExceptionHandler; 8 | 9 | @ControllerAdvice 10 | public class GlobalExceptionHandler { 11 | private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); 12 | 13 | @ExceptionHandler(ShortUrlNotFoundException.class) 14 | String handleShortUrlNotFoundException(ShortUrlNotFoundException ex) { 15 | log.error("Short URL not found: {}", ex.getMessage()); 16 | return "error/404"; 17 | } 18 | 19 | @ExceptionHandler(Exception.class) 20 | String handleException(Exception ex) { 21 | log.error("Unhandled exception: {}", ex.getMessage(), ex); 22 | return "error/500"; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /part-11/src/main/java/com/sivalabs/urlshortener/web/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web; 2 | 3 | import com.sivalabs.urlshortener.domain.exceptions.ShortUrlNotFoundException; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.web.bind.annotation.ControllerAdvice; 7 | import org.springframework.web.bind.annotation.ExceptionHandler; 8 | 9 | @ControllerAdvice 10 | public class GlobalExceptionHandler { 11 | private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); 12 | 13 | @ExceptionHandler(ShortUrlNotFoundException.class) 14 | String handleShortUrlNotFoundException(ShortUrlNotFoundException ex) { 15 | log.error("Short URL not found: {}", ex.getMessage()); 16 | return "error/404"; 17 | } 18 | 19 | @ExceptionHandler(Exception.class) 20 | String handleException(Exception ex) { 21 | log.error("Unhandled exception: {}", ex.getMessage(), ex); 22 | return "error/500"; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /part-12/src/main/java/com/sivalabs/urlshortener/web/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web; 2 | 3 | import com.sivalabs.urlshortener.domain.exceptions.ShortUrlNotFoundException; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.web.bind.annotation.ControllerAdvice; 7 | import org.springframework.web.bind.annotation.ExceptionHandler; 8 | 9 | @ControllerAdvice 10 | public class GlobalExceptionHandler { 11 | private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); 12 | 13 | @ExceptionHandler(ShortUrlNotFoundException.class) 14 | String handleShortUrlNotFoundException(ShortUrlNotFoundException ex) { 15 | log.error("Short URL not found: {}", ex.getMessage()); 16 | return "error/404"; 17 | } 18 | 19 | @ExceptionHandler(Exception.class) 20 | String handleException(Exception ex) { 21 | log.error("Unhandled exception: {}", ex.getMessage(), ex); 22 | return "error/500"; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /part-4/src/main/java/com/sivalabs/urlshortener/domain/services/ShortUrlService.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.ShortUrl; 4 | import com.sivalabs.urlshortener.domain.models.ShortUrlDto; 5 | import com.sivalabs.urlshortener.domain.repositories.ShortUrlRepository; 6 | import org.springframework.stereotype.Service; 7 | 8 | import java.util.List; 9 | 10 | @Service 11 | public class ShortUrlService { 12 | 13 | private final ShortUrlRepository shortUrlRepository; 14 | private final EntityMapper entityMapper; 15 | 16 | public ShortUrlService(ShortUrlRepository shortUrlRepository, EntityMapper entityMapper) { 17 | this.shortUrlRepository = shortUrlRepository; 18 | this.entityMapper = entityMapper; 19 | } 20 | 21 | public List findAllPublicShortUrls() { 22 | return shortUrlRepository.findPublicShortUrls() 23 | .stream().map(entityMapper::toShortUrlDto).toList(); 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /part-1/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot URL Shortener - Part 1 2 | 3 | ## Tech Stack 4 | * Spring Boot 5 | * Spring Security 6 | * JdbcClient / Spring Data JPA 7 | * PostgreSQL 8 | * FlywayDb Migrations 9 | * Thymeleaf 10 | * Bootstrap CSS 11 | 12 | ## What are you going to learn? 13 | * WebApp 14 | * Displaying Data 15 | * Form Submission 16 | * Validations 17 | * Handling Exceptions 18 | 19 | * Database 20 | * CRUD operations 21 | * DB migrations using Flyway 22 | * Pagination 23 | * Open Session In View Filter 24 | * Handling LazyLoadingException 25 | 26 | * Security 27 | * Login, Registration 28 | * Role Based Access Control 29 | * Get current user details 30 | 31 | ## Screenshots 32 | 33 | ### Home Page 34 | ![home.png](home.png) 35 | 36 | ### My URLs Page 37 | ![my-urls.png](my-urls.png) 38 | 39 | ### Admin Dashboard Page 40 | ![admin-dashboard.png](admin-dashboard.png) 41 | 42 | ### Login Page 43 | ![login.png](login.png) 44 | 45 | ### Registration Page 46 | ![register.png](register.png) 47 | 48 | -------------------------------------------------------------------------------- /part-10/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=only-script 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip 20 | -------------------------------------------------------------------------------- /part-11/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=only-script 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip 20 | -------------------------------------------------------------------------------- /part-12/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=only-script 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip 20 | -------------------------------------------------------------------------------- /part-2/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=only-script 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip 20 | -------------------------------------------------------------------------------- /part-3/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=only-script 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip 20 | -------------------------------------------------------------------------------- /part-4/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=only-script 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip 20 | -------------------------------------------------------------------------------- /part-5/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=only-script 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip 20 | -------------------------------------------------------------------------------- /part-6/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=only-script 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip 20 | -------------------------------------------------------------------------------- /part-7/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=only-script 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip 20 | -------------------------------------------------------------------------------- /part-8/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=only-script 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip 20 | -------------------------------------------------------------------------------- /part-9/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=only-script 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip 20 | -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/main/java/com/sivalabs/urlshortener/web/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web; 2 | 3 | import com.sivalabs.urlshortener.domain.exceptions.ShortUrlNotFoundException; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.web.bind.annotation.ControllerAdvice; 7 | import org.springframework.web.bind.annotation.ExceptionHandler; 8 | 9 | @ControllerAdvice 10 | public class GlobalExceptionHandler { 11 | private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); 12 | 13 | @ExceptionHandler(ShortUrlNotFoundException.class) 14 | String handleShortUrlNotFoundException(ShortUrlNotFoundException ex) { 15 | log.error("Short URL not found: {}", ex.getMessage()); 16 | return "error/404"; 17 | } 18 | 19 | @ExceptionHandler(Exception.class) 20 | String handleException(Exception ex) { 21 | log.error("Unhandled exception: {}", ex.getMessage(), ex); 22 | return "error/500"; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=only-script 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip 20 | -------------------------------------------------------------------------------- /part-7/src/main/java/com/sivalabs/urlshortener/web/controllers/SecurityUtils.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web.controllers; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.User; 4 | import com.sivalabs.urlshortener.domain.repositories.UserRepository; 5 | import org.springframework.security.core.Authentication; 6 | import org.springframework.security.core.context.SecurityContextHolder; 7 | import org.springframework.stereotype.Service; 8 | 9 | @Service 10 | public class SecurityUtils { 11 | 12 | private final UserRepository userRepository; 13 | 14 | public SecurityUtils(UserRepository userRepository) { 15 | this.userRepository = userRepository; 16 | } 17 | 18 | public User getCurrentUser() { 19 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 20 | if (authentication != null && authentication.isAuthenticated()) { 21 | String email = authentication.getName(); 22 | return userRepository.findByEmail(email).orElse(null); 23 | } 24 | return null; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /part-10/src/main/java/com/sivalabs/urlshortener/domain/repositories/ShortUrlRepository.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.repositories; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.ShortUrl; 4 | import org.springframework.data.domain.Page; 5 | import org.springframework.data.domain.Pageable; 6 | import org.springframework.data.jpa.repository.JpaRepository; 7 | import org.springframework.data.jpa.repository.Modifying; 8 | import org.springframework.data.jpa.repository.Query; 9 | 10 | import java.util.List; 11 | import java.util.Optional; 12 | 13 | public interface ShortUrlRepository extends JpaRepository { 14 | @Query("select su from ShortUrl su left join fetch su.createdBy where su.isPrivate = false") 15 | Page findPublicShortUrls(Pageable pageable); 16 | 17 | boolean existsByShortKey(String shortKey); 18 | 19 | Optional findByShortKey(String shortKey); 20 | 21 | Page findByCreatedById(Long userId, Pageable pageable); 22 | 23 | @Modifying 24 | void deleteByIdInAndCreatedById(List ids, Long userId); 25 | 26 | @Query("select u from ShortUrl u left join fetch u.createdBy") 27 | Page findAllShortUrls(Pageable pageable); 28 | } 29 | -------------------------------------------------------------------------------- /part-11/src/main/java/com/sivalabs/urlshortener/domain/repositories/ShortUrlRepository.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.repositories; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.ShortUrl; 4 | import org.springframework.data.domain.Page; 5 | import org.springframework.data.domain.Pageable; 6 | import org.springframework.data.jpa.repository.JpaRepository; 7 | import org.springframework.data.jpa.repository.Modifying; 8 | import org.springframework.data.jpa.repository.Query; 9 | 10 | import java.util.List; 11 | import java.util.Optional; 12 | 13 | public interface ShortUrlRepository extends JpaRepository { 14 | @Query("select su from ShortUrl su left join fetch su.createdBy where su.isPrivate = false") 15 | Page findPublicShortUrls(Pageable pageable); 16 | 17 | boolean existsByShortKey(String shortKey); 18 | 19 | Optional findByShortKey(String shortKey); 20 | 21 | Page findByCreatedById(Long userId, Pageable pageable); 22 | 23 | @Modifying 24 | void deleteByIdInAndCreatedById(List ids, Long userId); 25 | 26 | @Query("select u from ShortUrl u left join fetch u.createdBy") 27 | Page findAllShortUrls(Pageable pageable); 28 | } 29 | -------------------------------------------------------------------------------- /part-11/src/main/java/com/sivalabs/urlshortener/web/controllers/SecurityUtils.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web.controllers; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.User; 4 | import com.sivalabs.urlshortener.domain.repositories.UserRepository; 5 | import org.springframework.security.core.Authentication; 6 | import org.springframework.security.core.context.SecurityContextHolder; 7 | import org.springframework.stereotype.Service; 8 | 9 | @Service 10 | public class SecurityUtils { 11 | private final UserRepository userRepository; 12 | 13 | public SecurityUtils(UserRepository userRepository) { 14 | this.userRepository = userRepository; 15 | } 16 | 17 | public User getCurrentUser() { 18 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 19 | if (authentication != null && authentication.isAuthenticated()) { 20 | String email = authentication.getName(); 21 | return userRepository.findByEmail(email).orElse(null); 22 | } 23 | return null; 24 | } 25 | 26 | public Long getCurrentUserId() { 27 | User user = getCurrentUser(); 28 | return user != null ? user.getId() : null; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /part-12/src/main/java/com/sivalabs/urlshortener/domain/repositories/ShortUrlRepository.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.repositories; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.ShortUrl; 4 | import org.springframework.data.domain.Page; 5 | import org.springframework.data.domain.Pageable; 6 | import org.springframework.data.jpa.repository.JpaRepository; 7 | import org.springframework.data.jpa.repository.Modifying; 8 | import org.springframework.data.jpa.repository.Query; 9 | 10 | import java.util.List; 11 | import java.util.Optional; 12 | 13 | public interface ShortUrlRepository extends JpaRepository { 14 | @Query("select su from ShortUrl su left join fetch su.createdBy where su.isPrivate = false") 15 | Page findPublicShortUrls(Pageable pageable); 16 | 17 | boolean existsByShortKey(String shortKey); 18 | 19 | Optional findByShortKey(String shortKey); 20 | 21 | Page findByCreatedById(Long userId, Pageable pageable); 22 | 23 | @Modifying 24 | void deleteByIdInAndCreatedById(List ids, Long userId); 25 | 26 | @Query("select u from ShortUrl u left join fetch u.createdBy") 27 | Page findAllShortUrls(Pageable pageable); 28 | } 29 | -------------------------------------------------------------------------------- /part-12/src/main/java/com/sivalabs/urlshortener/web/controllers/SecurityUtils.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web.controllers; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.User; 4 | import com.sivalabs.urlshortener.domain.repositories.UserRepository; 5 | import org.springframework.security.core.Authentication; 6 | import org.springframework.security.core.context.SecurityContextHolder; 7 | import org.springframework.stereotype.Service; 8 | 9 | @Service 10 | public class SecurityUtils { 11 | private final UserRepository userRepository; 12 | 13 | public SecurityUtils(UserRepository userRepository) { 14 | this.userRepository = userRepository; 15 | } 16 | 17 | public User getCurrentUser() { 18 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 19 | if (authentication != null && authentication.isAuthenticated()) { 20 | String email = authentication.getName(); 21 | return userRepository.findByEmail(email).orElse(null); 22 | } 23 | return null; 24 | } 25 | 26 | public Long getCurrentUserId() { 27 | User user = getCurrentUser(); 28 | return user != null ? user.getId() : null; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/main/java/com/sivalabs/urlshortener/web/controllers/SecurityUtils.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web.controllers; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.User; 4 | import com.sivalabs.urlshortener.domain.services.UserService; 5 | import org.springframework.security.core.Authentication; 6 | import org.springframework.security.core.context.SecurityContextHolder; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | public class SecurityUtils { 11 | private final UserService userService; 12 | 13 | public SecurityUtils(UserService userService) { 14 | this.userService = userService; 15 | } 16 | 17 | public User getCurrentUser() { 18 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 19 | if (authentication != null && authentication.isAuthenticated()) { 20 | String email = authentication.getName(); 21 | return userService.findByEmail(email).orElse(null); 22 | } 23 | return null; 24 | } 25 | 26 | public Long getCurrentUserId() { 27 | User user = getCurrentUser(); 28 | return user != null ? user.getId() : null; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /part-10/src/main/java/com/sivalabs/urlshortener/domain/services/EntityMapper.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.ShortUrl; 4 | import com.sivalabs.urlshortener.domain.entities.User; 5 | import com.sivalabs.urlshortener.domain.models.ShortUrlDto; 6 | import com.sivalabs.urlshortener.domain.models.UserDto; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | public class EntityMapper { 11 | 12 | public ShortUrlDto toShortUrlDto(ShortUrl shortUrl) { 13 | UserDto userDto = null; 14 | if(shortUrl.getCreatedBy() != null) { 15 | userDto = toUserDto(shortUrl.getCreatedBy()); 16 | } 17 | 18 | return new ShortUrlDto( 19 | shortUrl.getId(), 20 | shortUrl.getShortKey(), 21 | shortUrl.getOriginalUrl(), 22 | shortUrl.getIsPrivate(), 23 | shortUrl.getExpiresAt(), 24 | userDto, 25 | shortUrl.getClickCount(), 26 | shortUrl.getCreatedAt() 27 | ); 28 | } 29 | 30 | public UserDto toUserDto(User user) { 31 | return new UserDto(user.getId(), user.getName()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /part-10/src/main/java/com/sivalabs/urlshortener/web/controllers/SecurityUtils.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web.controllers; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.User; 4 | import com.sivalabs.urlshortener.domain.repositories.UserRepository; 5 | import org.springframework.security.core.Authentication; 6 | import org.springframework.security.core.context.SecurityContextHolder; 7 | import org.springframework.stereotype.Service; 8 | 9 | @Service 10 | public class SecurityUtils { 11 | 12 | private final UserRepository userRepository; 13 | 14 | public SecurityUtils(UserRepository userRepository) { 15 | this.userRepository = userRepository; 16 | } 17 | 18 | public User getCurrentUser() { 19 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 20 | if (authentication != null && authentication.isAuthenticated()) { 21 | String email = authentication.getName(); 22 | return userRepository.findByEmail(email).orElse(null); 23 | } 24 | return null; 25 | } 26 | 27 | public Long getCurrentUserId() { 28 | User user = getCurrentUser(); 29 | return user != null ? user.getId() : null; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /part-11/src/main/java/com/sivalabs/urlshortener/domain/services/EntityMapper.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.ShortUrl; 4 | import com.sivalabs.urlshortener.domain.entities.User; 5 | import com.sivalabs.urlshortener.domain.models.ShortUrlDto; 6 | import com.sivalabs.urlshortener.domain.models.UserDto; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | public class EntityMapper { 11 | 12 | public ShortUrlDto toShortUrlDto(ShortUrl shortUrl) { 13 | UserDto userDto = null; 14 | if(shortUrl.getCreatedBy() != null) { 15 | userDto = toUserDto(shortUrl.getCreatedBy()); 16 | } 17 | 18 | return new ShortUrlDto( 19 | shortUrl.getId(), 20 | shortUrl.getShortKey(), 21 | shortUrl.getOriginalUrl(), 22 | shortUrl.getIsPrivate(), 23 | shortUrl.getExpiresAt(), 24 | userDto, 25 | shortUrl.getClickCount(), 26 | shortUrl.getCreatedAt() 27 | ); 28 | } 29 | 30 | public UserDto toUserDto(User user) { 31 | return new UserDto(user.getId(), user.getName()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /part-12/src/main/java/com/sivalabs/urlshortener/domain/services/EntityMapper.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.ShortUrl; 4 | import com.sivalabs.urlshortener.domain.entities.User; 5 | import com.sivalabs.urlshortener.domain.models.ShortUrlDto; 6 | import com.sivalabs.urlshortener.domain.models.UserDto; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | public class EntityMapper { 11 | 12 | public ShortUrlDto toShortUrlDto(ShortUrl shortUrl) { 13 | UserDto userDto = null; 14 | if(shortUrl.getCreatedBy() != null) { 15 | userDto = toUserDto(shortUrl.getCreatedBy()); 16 | } 17 | 18 | return new ShortUrlDto( 19 | shortUrl.getId(), 20 | shortUrl.getShortKey(), 21 | shortUrl.getOriginalUrl(), 22 | shortUrl.getIsPrivate(), 23 | shortUrl.getExpiresAt(), 24 | userDto, 25 | shortUrl.getClickCount(), 26 | shortUrl.getCreatedAt() 27 | ); 28 | } 29 | 30 | public UserDto toUserDto(User user) { 31 | return new UserDto(user.getId(), user.getName()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /part-4/src/main/java/com/sivalabs/urlshortener/domain/services/EntityMapper.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.ShortUrl; 4 | import com.sivalabs.urlshortener.domain.entities.User; 5 | import com.sivalabs.urlshortener.domain.models.ShortUrlDto; 6 | import com.sivalabs.urlshortener.domain.models.UserDto; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | public class EntityMapper { 11 | 12 | public ShortUrlDto toShortUrlDto(ShortUrl shortUrl) { 13 | UserDto userDto = null; 14 | if(shortUrl.getCreatedBy() != null) { 15 | userDto = toUserDto(shortUrl.getCreatedBy()); 16 | } 17 | 18 | return new ShortUrlDto( 19 | shortUrl.getId(), 20 | shortUrl.getShortKey(), 21 | shortUrl.getOriginalUrl(), 22 | shortUrl.getIsPrivate(), 23 | shortUrl.getExpiresAt(), 24 | userDto, 25 | shortUrl.getClickCount(), 26 | shortUrl.getCreatedAt() 27 | ); 28 | } 29 | 30 | public UserDto toUserDto(User user) { 31 | return new UserDto(user.getId(), user.getName()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /part-5/src/main/java/com/sivalabs/urlshortener/domain/services/EntityMapper.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.ShortUrl; 4 | import com.sivalabs.urlshortener.domain.entities.User; 5 | import com.sivalabs.urlshortener.domain.models.ShortUrlDto; 6 | import com.sivalabs.urlshortener.domain.models.UserDto; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | public class EntityMapper { 11 | 12 | public ShortUrlDto toShortUrlDto(ShortUrl shortUrl) { 13 | UserDto userDto = null; 14 | if(shortUrl.getCreatedBy() != null) { 15 | userDto = toUserDto(shortUrl.getCreatedBy()); 16 | } 17 | 18 | return new ShortUrlDto( 19 | shortUrl.getId(), 20 | shortUrl.getShortKey(), 21 | shortUrl.getOriginalUrl(), 22 | shortUrl.getIsPrivate(), 23 | shortUrl.getExpiresAt(), 24 | userDto, 25 | shortUrl.getClickCount(), 26 | shortUrl.getCreatedAt() 27 | ); 28 | } 29 | 30 | public UserDto toUserDto(User user) { 31 | return new UserDto(user.getId(), user.getName()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /part-6/src/main/java/com/sivalabs/urlshortener/domain/services/EntityMapper.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.ShortUrl; 4 | import com.sivalabs.urlshortener.domain.entities.User; 5 | import com.sivalabs.urlshortener.domain.models.ShortUrlDto; 6 | import com.sivalabs.urlshortener.domain.models.UserDto; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | public class EntityMapper { 11 | 12 | public ShortUrlDto toShortUrlDto(ShortUrl shortUrl) { 13 | UserDto userDto = null; 14 | if(shortUrl.getCreatedBy() != null) { 15 | userDto = toUserDto(shortUrl.getCreatedBy()); 16 | } 17 | 18 | return new ShortUrlDto( 19 | shortUrl.getId(), 20 | shortUrl.getShortKey(), 21 | shortUrl.getOriginalUrl(), 22 | shortUrl.getIsPrivate(), 23 | shortUrl.getExpiresAt(), 24 | userDto, 25 | shortUrl.getClickCount(), 26 | shortUrl.getCreatedAt() 27 | ); 28 | } 29 | 30 | public UserDto toUserDto(User user) { 31 | return new UserDto(user.getId(), user.getName()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /part-7/src/main/java/com/sivalabs/urlshortener/domain/services/EntityMapper.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.ShortUrl; 4 | import com.sivalabs.urlshortener.domain.entities.User; 5 | import com.sivalabs.urlshortener.domain.models.ShortUrlDto; 6 | import com.sivalabs.urlshortener.domain.models.UserDto; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | public class EntityMapper { 11 | 12 | public ShortUrlDto toShortUrlDto(ShortUrl shortUrl) { 13 | UserDto userDto = null; 14 | if(shortUrl.getCreatedBy() != null) { 15 | userDto = toUserDto(shortUrl.getCreatedBy()); 16 | } 17 | 18 | return new ShortUrlDto( 19 | shortUrl.getId(), 20 | shortUrl.getShortKey(), 21 | shortUrl.getOriginalUrl(), 22 | shortUrl.getIsPrivate(), 23 | shortUrl.getExpiresAt(), 24 | userDto, 25 | shortUrl.getClickCount(), 26 | shortUrl.getCreatedAt() 27 | ); 28 | } 29 | 30 | public UserDto toUserDto(User user) { 31 | return new UserDto(user.getId(), user.getName()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /part-8/src/main/java/com/sivalabs/urlshortener/domain/services/EntityMapper.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.ShortUrl; 4 | import com.sivalabs.urlshortener.domain.entities.User; 5 | import com.sivalabs.urlshortener.domain.models.ShortUrlDto; 6 | import com.sivalabs.urlshortener.domain.models.UserDto; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | public class EntityMapper { 11 | 12 | public ShortUrlDto toShortUrlDto(ShortUrl shortUrl) { 13 | UserDto userDto = null; 14 | if(shortUrl.getCreatedBy() != null) { 15 | userDto = toUserDto(shortUrl.getCreatedBy()); 16 | } 17 | 18 | return new ShortUrlDto( 19 | shortUrl.getId(), 20 | shortUrl.getShortKey(), 21 | shortUrl.getOriginalUrl(), 22 | shortUrl.getIsPrivate(), 23 | shortUrl.getExpiresAt(), 24 | userDto, 25 | shortUrl.getClickCount(), 26 | shortUrl.getCreatedAt() 27 | ); 28 | } 29 | 30 | public UserDto toUserDto(User user) { 31 | return new UserDto(user.getId(), user.getName()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /part-8/src/main/java/com/sivalabs/urlshortener/web/controllers/SecurityUtils.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web.controllers; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.User; 4 | import com.sivalabs.urlshortener.domain.repositories.UserRepository; 5 | import org.springframework.security.core.Authentication; 6 | import org.springframework.security.core.context.SecurityContextHolder; 7 | import org.springframework.stereotype.Service; 8 | 9 | @Service 10 | public class SecurityUtils { 11 | 12 | private final UserRepository userRepository; 13 | 14 | public SecurityUtils(UserRepository userRepository) { 15 | this.userRepository = userRepository; 16 | } 17 | 18 | public User getCurrentUser() { 19 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 20 | if (authentication != null && authentication.isAuthenticated()) { 21 | String email = authentication.getName(); 22 | return userRepository.findByEmail(email).orElse(null); 23 | } 24 | return null; 25 | } 26 | 27 | public Long getCurrentUserId() { 28 | User user = getCurrentUser(); 29 | return user != null ? user.getId() : null; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /part-9/src/main/java/com/sivalabs/urlshortener/domain/services/EntityMapper.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.ShortUrl; 4 | import com.sivalabs.urlshortener.domain.entities.User; 5 | import com.sivalabs.urlshortener.domain.models.ShortUrlDto; 6 | import com.sivalabs.urlshortener.domain.models.UserDto; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | public class EntityMapper { 11 | 12 | public ShortUrlDto toShortUrlDto(ShortUrl shortUrl) { 13 | UserDto userDto = null; 14 | if(shortUrl.getCreatedBy() != null) { 15 | userDto = toUserDto(shortUrl.getCreatedBy()); 16 | } 17 | 18 | return new ShortUrlDto( 19 | shortUrl.getId(), 20 | shortUrl.getShortKey(), 21 | shortUrl.getOriginalUrl(), 22 | shortUrl.getIsPrivate(), 23 | shortUrl.getExpiresAt(), 24 | userDto, 25 | shortUrl.getClickCount(), 26 | shortUrl.getCreatedAt() 27 | ); 28 | } 29 | 30 | public UserDto toUserDto(User user) { 31 | return new UserDto(user.getId(), user.getName()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /part-9/src/main/java/com/sivalabs/urlshortener/web/controllers/SecurityUtils.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.web.controllers; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.User; 4 | import com.sivalabs.urlshortener.domain.repositories.UserRepository; 5 | import org.springframework.security.core.Authentication; 6 | import org.springframework.security.core.context.SecurityContextHolder; 7 | import org.springframework.stereotype.Service; 8 | 9 | @Service 10 | public class SecurityUtils { 11 | 12 | private final UserRepository userRepository; 13 | 14 | public SecurityUtils(UserRepository userRepository) { 15 | this.userRepository = userRepository; 16 | } 17 | 18 | public User getCurrentUser() { 19 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 20 | if (authentication != null && authentication.isAuthenticated()) { 21 | String email = authentication.getName(); 22 | return userRepository.findByEmail(email).orElse(null); 23 | } 24 | return null; 25 | } 26 | 27 | public Long getCurrentUserId() { 28 | User user = getCurrentUser(); 29 | return user != null ? user.getId() : null; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /part-10/src/main/java/com/sivalabs/urlshortener/domain/services/UrlExistenceValidator.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.net.HttpURLConnection; 7 | import java.net.URI; 8 | import java.net.URL; 9 | 10 | public class UrlExistenceValidator { 11 | private static final Logger log = LoggerFactory.getLogger(UrlExistenceValidator.class); 12 | 13 | public static boolean isUrlExists(String urlString) { 14 | try { 15 | log.debug("Checking if URL exists: {}", urlString); 16 | URL url = new URI(urlString).toURL(); 17 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 18 | connection.setRequestMethod("HEAD"); 19 | connection.setConnectTimeout(5000); // 5 seconds 20 | connection.setReadTimeout(5000); 21 | 22 | int responseCode = connection.getResponseCode(); 23 | return (responseCode >= 200 && responseCode < 400); // 2xx and 3xx are valid 24 | } catch (Exception e) { 25 | log.error("Error while checking URL: {}", urlString, e); 26 | return false; // URL is invalid or not reachable 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /part-11/src/main/java/com/sivalabs/urlshortener/domain/services/UrlExistenceValidator.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.net.HttpURLConnection; 7 | import java.net.URI; 8 | import java.net.URL; 9 | 10 | public class UrlExistenceValidator { 11 | private static final Logger log = LoggerFactory.getLogger(UrlExistenceValidator.class); 12 | 13 | public static boolean isUrlExists(String urlString) { 14 | try { 15 | log.debug("Checking if URL exists: {}", urlString); 16 | URL url = new URI(urlString).toURL(); 17 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 18 | connection.setRequestMethod("HEAD"); 19 | connection.setConnectTimeout(5000); // 5 seconds 20 | connection.setReadTimeout(5000); 21 | 22 | int responseCode = connection.getResponseCode(); 23 | return (responseCode >= 200 && responseCode < 400); // 2xx and 3xx are valid 24 | } catch (Exception e) { 25 | log.error("Error while checking URL: {}", urlString, e); 26 | return false; // URL is invalid or not reachable 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /part-12/src/main/java/com/sivalabs/urlshortener/domain/services/UrlExistenceValidator.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.net.HttpURLConnection; 7 | import java.net.URI; 8 | import java.net.URL; 9 | 10 | public class UrlExistenceValidator { 11 | private static final Logger log = LoggerFactory.getLogger(UrlExistenceValidator.class); 12 | 13 | public static boolean isUrlExists(String urlString) { 14 | try { 15 | log.debug("Checking if URL exists: {}", urlString); 16 | URL url = new URI(urlString).toURL(); 17 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 18 | connection.setRequestMethod("HEAD"); 19 | connection.setConnectTimeout(5000); // 5 seconds 20 | connection.setReadTimeout(5000); 21 | 22 | int responseCode = connection.getResponseCode(); 23 | return (responseCode >= 200 && responseCode < 400); // 2xx and 3xx are valid 24 | } catch (Exception e) { 25 | log.error("Error while checking URL: {}", urlString, e); 26 | return false; // URL is invalid or not reachable 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /part-5/src/main/java/com/sivalabs/urlshortener/domain/services/UrlExistenceValidator.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.net.HttpURLConnection; 7 | import java.net.URI; 8 | import java.net.URL; 9 | 10 | public class UrlExistenceValidator { 11 | private static final Logger log = LoggerFactory.getLogger(UrlExistenceValidator.class); 12 | 13 | public static boolean isUrlExists(String urlString) { 14 | try { 15 | log.debug("Checking if URL exists: {}", urlString); 16 | URL url = new URI(urlString).toURL(); 17 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 18 | connection.setRequestMethod("HEAD"); 19 | connection.setConnectTimeout(5000); // 5 seconds 20 | connection.setReadTimeout(5000); 21 | 22 | int responseCode = connection.getResponseCode(); 23 | return (responseCode >= 200 && responseCode < 400); // 2xx and 3xx are valid 24 | } catch (Exception e) { 25 | log.error("Error while checking URL: {}", urlString, e); 26 | return false; // URL is invalid or not reachable 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /part-6/src/main/java/com/sivalabs/urlshortener/domain/services/UrlExistenceValidator.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.net.HttpURLConnection; 7 | import java.net.URI; 8 | import java.net.URL; 9 | 10 | public class UrlExistenceValidator { 11 | private static final Logger log = LoggerFactory.getLogger(UrlExistenceValidator.class); 12 | 13 | public static boolean isUrlExists(String urlString) { 14 | try { 15 | log.debug("Checking if URL exists: {}", urlString); 16 | URL url = new URI(urlString).toURL(); 17 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 18 | connection.setRequestMethod("HEAD"); 19 | connection.setConnectTimeout(5000); // 5 seconds 20 | connection.setReadTimeout(5000); 21 | 22 | int responseCode = connection.getResponseCode(); 23 | return (responseCode >= 200 && responseCode < 400); // 2xx and 3xx are valid 24 | } catch (Exception e) { 25 | log.error("Error while checking URL: {}", urlString, e); 26 | return false; // URL is invalid or not reachable 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /part-7/src/main/java/com/sivalabs/urlshortener/domain/services/UrlExistenceValidator.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.net.HttpURLConnection; 7 | import java.net.URI; 8 | import java.net.URL; 9 | 10 | public class UrlExistenceValidator { 11 | private static final Logger log = LoggerFactory.getLogger(UrlExistenceValidator.class); 12 | 13 | public static boolean isUrlExists(String urlString) { 14 | try { 15 | log.debug("Checking if URL exists: {}", urlString); 16 | URL url = new URI(urlString).toURL(); 17 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 18 | connection.setRequestMethod("HEAD"); 19 | connection.setConnectTimeout(5000); // 5 seconds 20 | connection.setReadTimeout(5000); 21 | 22 | int responseCode = connection.getResponseCode(); 23 | return (responseCode >= 200 && responseCode < 400); // 2xx and 3xx are valid 24 | } catch (Exception e) { 25 | log.error("Error while checking URL: {}", urlString, e); 26 | return false; // URL is invalid or not reachable 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /part-8/src/main/java/com/sivalabs/urlshortener/domain/services/UrlExistenceValidator.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.net.HttpURLConnection; 7 | import java.net.URI; 8 | import java.net.URL; 9 | 10 | public class UrlExistenceValidator { 11 | private static final Logger log = LoggerFactory.getLogger(UrlExistenceValidator.class); 12 | 13 | public static boolean isUrlExists(String urlString) { 14 | try { 15 | log.debug("Checking if URL exists: {}", urlString); 16 | URL url = new URI(urlString).toURL(); 17 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 18 | connection.setRequestMethod("HEAD"); 19 | connection.setConnectTimeout(5000); // 5 seconds 20 | connection.setReadTimeout(5000); 21 | 22 | int responseCode = connection.getResponseCode(); 23 | return (responseCode >= 200 && responseCode < 400); // 2xx and 3xx are valid 24 | } catch (Exception e) { 25 | log.error("Error while checking URL: {}", urlString, e); 26 | return false; // URL is invalid or not reachable 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /part-9/src/main/java/com/sivalabs/urlshortener/domain/services/UrlExistenceValidator.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.net.HttpURLConnection; 7 | import java.net.URI; 8 | import java.net.URL; 9 | 10 | public class UrlExistenceValidator { 11 | private static final Logger log = LoggerFactory.getLogger(UrlExistenceValidator.class); 12 | 13 | public static boolean isUrlExists(String urlString) { 14 | try { 15 | log.debug("Checking if URL exists: {}", urlString); 16 | URL url = new URI(urlString).toURL(); 17 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 18 | connection.setRequestMethod("HEAD"); 19 | connection.setConnectTimeout(5000); // 5 seconds 20 | connection.setReadTimeout(5000); 21 | 22 | int responseCode = connection.getResponseCode(); 23 | return (responseCode >= 200 && responseCode < 400); // 2xx and 3xx are valid 24 | } catch (Exception e) { 25 | log.error("Error while checking URL: {}", urlString, e); 26 | return false; // URL is invalid or not reachable 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/main/java/com/sivalabs/urlshortener/domain/repositories/ShortUrlRepository.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.repositories; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.ShortUrl; 4 | import org.springframework.data.domain.Page; 5 | import org.springframework.data.domain.Pageable; 6 | import org.springframework.data.jpa.repository.JpaRepository; 7 | import org.springframework.data.jpa.repository.Modifying; 8 | import org.springframework.data.jpa.repository.Query; 9 | 10 | import java.util.List; 11 | import java.util.Optional; 12 | 13 | public interface ShortUrlRepository extends JpaRepository { 14 | @Query("select su from ShortUrl su left join fetch su.createdBy where su.isPrivate = false") 15 | Page findPublicShortUrls(Pageable pageable); 16 | 17 | boolean existsByShortKey(String shortKey); 18 | 19 | Optional findByShortKey(String shortKey); 20 | 21 | Page findByCreatedById(Long userId, Pageable pageable); 22 | 23 | @Modifying 24 | void deleteByIdInAndCreatedById(List ids, Long userId); 25 | 26 | @Query("select u from ShortUrl u left join fetch u.createdBy") 27 | Page findAllShortUrls(Pageable pageable); 28 | } 29 | -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/main/java/com/sivalabs/urlshortener/domain/services/EntityMapper.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.ShortUrl; 4 | import com.sivalabs.urlshortener.domain.entities.User; 5 | import com.sivalabs.urlshortener.domain.models.ShortUrlDto; 6 | import com.sivalabs.urlshortener.domain.models.UserDto; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | public class EntityMapper { 11 | 12 | public ShortUrlDto toShortUrlDto(ShortUrl shortUrl) { 13 | UserDto userDto = null; 14 | if(shortUrl.getCreatedBy() != null) { 15 | userDto = toUserDto(shortUrl.getCreatedBy()); 16 | } 17 | 18 | return new ShortUrlDto( 19 | shortUrl.getId(), 20 | shortUrl.getShortKey(), 21 | shortUrl.getOriginalUrl(), 22 | shortUrl.getIsPrivate(), 23 | shortUrl.getExpiresAt(), 24 | userDto, 25 | shortUrl.getClickCount(), 26 | shortUrl.getCreatedAt() 27 | ); 28 | } 29 | 30 | public UserDto toUserDto(User user) { 31 | return new UserDto(user.getId(), user.getName()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot URL Shortener 2 | URL Shortener is a service that shortens a long URL into a short URL which is easier to share. 3 | 4 | This repository contains the source code for the [Spring Boot URL Shortener Course](https://www.youtube.com/playlist?list=PLuNxlOYbv61gfJv1uTOFKAWig98e7fk_m). 5 | 6 | ![Spring Boot URL Shortener course](springboot-url-shortener.png) 7 | 8 | 9 | ## Requirements 10 | 1. Shorten URL: 11 | * Accept a long URL and return a shortened URL. 12 | * The shortened URL should be unique. 13 | * Validate the input URL for correctness (optional/configurable). 14 | * Allow guest users to create public shortened URLs with the default 30-day expiration. 15 | * Allow authenticated users: 16 | * Create public or private shortened URLs with custom expiration time. 17 | * View and delete their shortened URLs. 18 | 19 | 2. Redirection: 20 | * When a shortened URL is accessed, it should redirect to the original long URL. 21 | * Handle invalid or expired shortened URLs gracefully. 22 | 23 | 3. Analytics: 24 | * Track the number of clicks for each shortened URL. 25 | 26 | 4. User Management: 27 | * Allow users to register and login. 28 | * Admin users can view any shortened URLs(including public and private URLs). 29 | 30 | -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/main/java/com/sivalabs/urlshortener/domain/services/UrlExistenceValidator.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.net.HttpURLConnection; 7 | import java.net.URI; 8 | import java.net.URL; 9 | 10 | public class UrlExistenceValidator { 11 | private static final Logger log = LoggerFactory.getLogger(UrlExistenceValidator.class); 12 | 13 | public static boolean isUrlExists(String urlString) { 14 | try { 15 | log.debug("Checking if URL exists: {}", urlString); 16 | URL url = new URI(urlString).toURL(); 17 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 18 | connection.setRequestMethod("HEAD"); 19 | connection.setConnectTimeout(5000); // 5 seconds 20 | connection.setReadTimeout(5000); 21 | 22 | int responseCode = connection.getResponseCode(); 23 | return (responseCode >= 200 && responseCode < 400); // 2xx and 3xx are valid 24 | } catch (Exception e) { 25 | log.error("Error while checking URL: {}", urlString, e); 26 | return false; // URL is invalid or not reachable 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /part-11/src/main/java/com/sivalabs/urlshortener/domain/services/UserService.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.User; 4 | import com.sivalabs.urlshortener.domain.models.CreateUserCmd; 5 | import com.sivalabs.urlshortener.domain.models.Role; 6 | import com.sivalabs.urlshortener.domain.repositories.UserRepository; 7 | import org.springframework.security.crypto.password.PasswordEncoder; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.transaction.annotation.Transactional; 10 | 11 | import java.time.Instant; 12 | 13 | @Service 14 | @Transactional(readOnly = true) 15 | public class UserService { 16 | private final UserRepository userRepository; 17 | private final PasswordEncoder passwordEncoder; 18 | 19 | public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder) { 20 | this.userRepository = userRepository; 21 | this.passwordEncoder = passwordEncoder; 22 | } 23 | 24 | @Transactional 25 | public void createUser(CreateUserCmd cmd) { 26 | if (userRepository.existsByEmail(cmd.email())) { 27 | throw new RuntimeException("Email already exists"); 28 | } 29 | var user = new User(); 30 | user.setEmail(cmd.email()); 31 | user.setPassword(passwordEncoder.encode(cmd.password())); 32 | user.setName(cmd.name()); 33 | user.setRole(cmd.role()); 34 | user.setCreatedAt(Instant.now()); 35 | userRepository.save(user); 36 | } 37 | } -------------------------------------------------------------------------------- /part-12/src/main/java/com/sivalabs/urlshortener/domain/services/UserService.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.User; 4 | import com.sivalabs.urlshortener.domain.models.CreateUserCmd; 5 | import com.sivalabs.urlshortener.domain.models.Role; 6 | import com.sivalabs.urlshortener.domain.repositories.UserRepository; 7 | import org.springframework.security.crypto.password.PasswordEncoder; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.transaction.annotation.Transactional; 10 | 11 | import java.time.Instant; 12 | 13 | @Service 14 | @Transactional(readOnly = true) 15 | public class UserService { 16 | private final UserRepository userRepository; 17 | private final PasswordEncoder passwordEncoder; 18 | 19 | public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder) { 20 | this.userRepository = userRepository; 21 | this.passwordEncoder = passwordEncoder; 22 | } 23 | 24 | @Transactional 25 | public void createUser(CreateUserCmd cmd) { 26 | if (userRepository.existsByEmail(cmd.email())) { 27 | throw new RuntimeException("Email already exists"); 28 | } 29 | var user = new User(); 30 | user.setEmail(cmd.email()); 31 | user.setPassword(passwordEncoder.encode(cmd.password())); 32 | user.setName(cmd.name()); 33 | user.setRole(cmd.role()); 34 | user.setCreatedAt(Instant.now()); 35 | userRepository.save(user); 36 | } 37 | } -------------------------------------------------------------------------------- /part-7/src/main/java/com/sivalabs/urlshortener/domain/services/SecurityUserDetailsService.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.User; 4 | import com.sivalabs.urlshortener.domain.repositories.UserRepository; 5 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 6 | import org.springframework.security.core.userdetails.UserDetails; 7 | import org.springframework.security.core.userdetails.UserDetailsService; 8 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.util.List; 12 | 13 | @Service 14 | public class SecurityUserDetailsService implements UserDetailsService { 15 | private final UserRepository userRepository; 16 | 17 | public SecurityUserDetailsService(UserRepository userRepository) { 18 | this.userRepository = userRepository; 19 | } 20 | 21 | @Override 22 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 23 | User user = userRepository.findByEmail(username) 24 | .orElseThrow( 25 | () -> new UsernameNotFoundException("User not found with email: " + username) 26 | ); 27 | return new org.springframework.security.core.userdetails.User( 28 | user.getEmail(), 29 | user.getPassword(), 30 | List.of(new SimpleGrantedAuthority(user.getRole().name())) 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /part-8/src/main/java/com/sivalabs/urlshortener/domain/services/SecurityUserDetailsService.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.User; 4 | import com.sivalabs.urlshortener.domain.repositories.UserRepository; 5 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 6 | import org.springframework.security.core.userdetails.UserDetails; 7 | import org.springframework.security.core.userdetails.UserDetailsService; 8 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.util.List; 12 | 13 | @Service 14 | public class SecurityUserDetailsService implements UserDetailsService { 15 | private final UserRepository userRepository; 16 | 17 | public SecurityUserDetailsService(UserRepository userRepository) { 18 | this.userRepository = userRepository; 19 | } 20 | 21 | @Override 22 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 23 | User user = userRepository.findByEmail(username) 24 | .orElseThrow( 25 | () -> new UsernameNotFoundException("User not found with email: " + username) 26 | ); 27 | return new org.springframework.security.core.userdetails.User( 28 | user.getEmail(), 29 | user.getPassword(), 30 | List.of(new SimpleGrantedAuthority(user.getRole().name())) 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /part-9/src/main/java/com/sivalabs/urlshortener/domain/services/SecurityUserDetailsService.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.User; 4 | import com.sivalabs.urlshortener.domain.repositories.UserRepository; 5 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 6 | import org.springframework.security.core.userdetails.UserDetails; 7 | import org.springframework.security.core.userdetails.UserDetailsService; 8 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.util.List; 12 | 13 | @Service 14 | public class SecurityUserDetailsService implements UserDetailsService { 15 | private final UserRepository userRepository; 16 | 17 | public SecurityUserDetailsService(UserRepository userRepository) { 18 | this.userRepository = userRepository; 19 | } 20 | 21 | @Override 22 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 23 | User user = userRepository.findByEmail(username) 24 | .orElseThrow( 25 | () -> new UsernameNotFoundException("User not found with email: " + username) 26 | ); 27 | return new org.springframework.security.core.userdetails.User( 28 | user.getEmail(), 29 | user.getPassword(), 30 | List.of(new SimpleGrantedAuthority(user.getRole().name())) 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /part-10/src/main/java/com/sivalabs/urlshortener/domain/services/SecurityUserDetailsService.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.User; 4 | import com.sivalabs.urlshortener.domain.repositories.UserRepository; 5 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 6 | import org.springframework.security.core.userdetails.UserDetails; 7 | import org.springframework.security.core.userdetails.UserDetailsService; 8 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.util.List; 12 | 13 | @Service 14 | public class SecurityUserDetailsService implements UserDetailsService { 15 | private final UserRepository userRepository; 16 | 17 | public SecurityUserDetailsService(UserRepository userRepository) { 18 | this.userRepository = userRepository; 19 | } 20 | 21 | @Override 22 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 23 | User user = userRepository.findByEmail(username) 24 | .orElseThrow( 25 | () -> new UsernameNotFoundException("User not found with email: " + username) 26 | ); 27 | return new org.springframework.security.core.userdetails.User( 28 | user.getEmail(), 29 | user.getPassword(), 30 | List.of(new SimpleGrantedAuthority(user.getRole().name())) 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /part-11/src/main/java/com/sivalabs/urlshortener/domain/services/SecurityUserDetailsService.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.User; 4 | import com.sivalabs.urlshortener.domain.repositories.UserRepository; 5 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 6 | import org.springframework.security.core.userdetails.UserDetails; 7 | import org.springframework.security.core.userdetails.UserDetailsService; 8 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.util.List; 12 | 13 | @Service 14 | public class SecurityUserDetailsService implements UserDetailsService { 15 | private final UserRepository userRepository; 16 | 17 | public SecurityUserDetailsService(UserRepository userRepository) { 18 | this.userRepository = userRepository; 19 | } 20 | 21 | @Override 22 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 23 | User user = userRepository.findByEmail(username) 24 | .orElseThrow( 25 | () -> new UsernameNotFoundException("User not found with email: " + username) 26 | ); 27 | return new org.springframework.security.core.userdetails.User( 28 | user.getEmail(), 29 | user.getPassword(), 30 | List.of(new SimpleGrantedAuthority(user.getRole().name())) 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /part-12/src/main/java/com/sivalabs/urlshortener/domain/services/SecurityUserDetailsService.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.User; 4 | import com.sivalabs.urlshortener.domain.repositories.UserRepository; 5 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 6 | import org.springframework.security.core.userdetails.UserDetails; 7 | import org.springframework.security.core.userdetails.UserDetailsService; 8 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.util.List; 12 | 13 | @Service 14 | public class SecurityUserDetailsService implements UserDetailsService { 15 | private final UserRepository userRepository; 16 | 17 | public SecurityUserDetailsService(UserRepository userRepository) { 18 | this.userRepository = userRepository; 19 | } 20 | 21 | @Override 22 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 23 | User user = userRepository.findByEmail(username) 24 | .orElseThrow( 25 | () -> new UsernameNotFoundException("User not found with email: " + username) 26 | ); 27 | return new org.springframework.security.core.userdetails.User( 28 | user.getEmail(), 29 | user.getPassword(), 30 | List.of(new SimpleGrantedAuthority(user.getRole().name())) 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /spring-boot-url-shortener-final/src/main/java/com/sivalabs/urlshortener/domain/services/SecurityUserDetailsService.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.urlshortener.domain.services; 2 | 3 | import com.sivalabs.urlshortener.domain.entities.User; 4 | import com.sivalabs.urlshortener.domain.repositories.UserRepository; 5 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 6 | import org.springframework.security.core.userdetails.UserDetails; 7 | import org.springframework.security.core.userdetails.UserDetailsService; 8 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.util.List; 12 | 13 | @Service 14 | public class SecurityUserDetailsService implements UserDetailsService { 15 | private final UserRepository userRepository; 16 | 17 | public SecurityUserDetailsService(UserRepository userRepository) { 18 | this.userRepository = userRepository; 19 | } 20 | 21 | @Override 22 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 23 | User user = userRepository.findByEmail(username) 24 | .orElseThrow( 25 | () -> new UsernameNotFoundException("User not found with email: " + username) 26 | ); 27 | return new org.springframework.security.core.userdetails.User( 28 | user.getEmail(), 29 | user.getPassword(), 30 | List.of(new SimpleGrantedAuthority(user.getRole().name())) 31 | ); 32 | } 33 | } 34 | --------------------------------------------------------------------------------