Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .dockeringnore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
target/
.git/
.idea/
*.iml
*.log
.DS_Store
23 changes: 23 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
FROM gradle:8.5-jdk17 AS builder

WORKDIR /app
COPY build.gradle.kts settings.gradle.kts ./
COPY gradle gradle
RUN gradle dependencies || return 0

COPY . .

RUN gradle bootJar --no-daemon

FROM eclipse-temurin:17-jre-alpine

RUN adduser -D spring
USER spring

WORKDIR /home/spring

COPY --from=builder /app/build/libs/*.jar app.jar

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "app.jar"]
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ version = "0.0.2"

java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
languageVersion = JavaLanguageVersion.of(17)
}
}

Expand Down Expand Up @@ -45,6 +45,7 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
compileOnly("org.projectlombok:lombok")
developmentOnly("org.springframework.boot:spring-boot-devtools")
implementation("org.postgresql:postgresql:42.7.3")
runtimeOnly("org.postgresql:postgresql")
annotationProcessor("org.projectlombok:lombok")
testImplementation("org.springframework.boot:spring-boot-starter-test")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,20 @@
import java.util.Optional;

import jakarta.servlet.http.HttpServletRequest;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/linshor/v1")
@AllArgsConstructor
@Tag(name = "URL Shortener API", description = "Operations for URL shortening and redirection")
public class LinShorController {
private final LinShorService linShorService;

public LinShorController(@Qualifier("dbLinShorService") LinShorService linShorService) {
this.linShorService = linShorService;
}

@PutMapping("/update")
@Operation(summary = "Update a long URL",
description = "Updates an existing shortened URL with a new long URL.")
Expand Down Expand Up @@ -78,7 +81,7 @@ public ResponseEntity<Object> redirectToLongUrl(@PathVariable @Parameter(
String longUrl = mapping.get().getLongUrl().trim().replaceAll("^\"|\"$", "");

try {
URI uri = new URL(longUrl).toURI(); // Convert URL to URI safely
URI uri = new URL(longUrl).toURI();
return ResponseEntity.status(302).location(uri).build();
} catch (Exception e) {
return ResponseEntity.badRequest().body("Invalid URL stored in database: " + longUrl);
Expand Down
9 changes: 5 additions & 4 deletions src/main/java/com/m/linshor/entities/Mapping.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
package com.m.linshor.entities;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.*;
import lombok.*;

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@Entity
@Table(name = "mapping")
public class Mapping {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
int id;

@Column(unique = true, nullable = false)
String longUrl;

@Column(nullable = false, length = 2048)
String shortUrl;
}
63 changes: 63 additions & 0 deletions src/main/java/com/m/linshor/services/DbLinShorService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.m.linshor.services;

import com.m.linshor.entities.Mapping;
import com.m.linshor.repositories.MappingRepository;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

import java.util.Optional;
import java.util.Random;

@Service
@Primary
@AllArgsConstructor
public class DbLinShorService implements LinShorService {
private final MappingRepository repository;
private static final String BASE62 =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
private static final int SHORT_URL_LENGTH = 10;

private String generateShor() {
Random random = new Random();
StringBuilder sb = new StringBuilder(SHORT_URL_LENGTH);
for (int i = 0; i < SHORT_URL_LENGTH; i++) {
sb.append(BASE62.charAt(random.nextInt(BASE62.length())));
}
return sb.toString();
}

public Mapping saveLink(String longUrl) {
String shortUrl;
do {
shortUrl = generateShor();
} while (repository.findByShortUrl(shortUrl).isPresent());

Mapping urlMapping = new Mapping();
urlMapping.setShortUrl(shortUrl);
urlMapping.setLongUrl(longUrl);
return repository.save(urlMapping);
}

@Override
public Mapping updateLink(String longUrl) {
Mapping mapping = findByLongUrl(longUrl);
String shortUrl = generateShor();

mapping.setShortUrl(shortUrl);
return repository.save(mapping);
}

public Optional<Mapping> findByShortUrl(String shortUrl) {
return repository.findByShortUrl(shortUrl);
}

public void deleteByShortUrl(String shortUrl) {
repository.findByShortUrl(shortUrl).ifPresent(repository::delete);
}

@Override
public Mapping findByLongUrl(String longUrl) {
return repository.findByLongUrl(longUrl);
}
}
10 changes: 6 additions & 4 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
spring.application.name=LinShor
spring.datasource.url=jdbc:postgresql://localhost:5432/template1
spring.datasource.url=jdbc:postgresql://linshor-db:5432/linshor
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.datasource.driver-class-name=org.postgresql.Driver

spring.datasource.hikari.minimum-idle=2
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.idle-timeout=30000
spring.datasource.hikari.pool-name=LinShorHikariCP