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
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ jobs:
DB_URL: jdbc:postgresql://localhost:5432/testdb
DB_USERNAME: postgres
DB_PASSWORD: postgres
RESEND_API: test-key-not-real
B2_ACCESS_KEY: test-key-not-real
B2_SECRET_KEY: test-secret-not-real
B2_BUCKET_NAME: test-bucket

build-and-push:
runs-on: ubuntu-latest
Expand Down
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,16 @@

**Header:** `Cookie: session_id=<SESSION_ID>`

**→ 200:** returns user (`id`, `email`, `created_at`)
**→ 200:** returns user (`id`, `email`, `created_at`)



# Image Endpoints



- `POST /images/upload` Upload image
- `GET /images/my` My uploaded images
- `GET /images` Home / search images **_Public_**
- `GET /images/{id}/content` Display image **_Public_**
- `DELETE /images/{id}` Delete my image
9 changes: 8 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,20 @@


<!-- Spring Security Dependency -->

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>


<!-- AWS SDK dependency -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
<version>2.25.0</version>
</dependency>


</dependencies>

<build>
Expand Down
41 changes: 41 additions & 0 deletions src/main/java/dev/barboud/taglens/configuration/B2Config.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package dev.barboud.taglens.configuration;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;

import java.net.URI;

@Configuration
public class B2Config {

@Value("${b2.endpoint}") private String endpoint;
@Value("${b2.region}") private String region;
@Value("${b2.access-key}") private String accessKey;
@Value("${b2.secret-key}") private String secretKey;

@Bean
public S3Client s3Client() {
return S3Client.builder()
.credentialsProvider(StaticCredentialsProvider.create(
AwsBasicCredentials.create(accessKey, secretKey)))
.endpointOverride(URI.create(endpoint))
.region(Region.of(region))
.build();
}

@Bean
public S3Presigner s3Presigner() {
return S3Presigner.builder()
.credentialsProvider(StaticCredentialsProvider.create(
AwsBasicCredentials.create(accessKey, secretKey)))
.endpointOverride(URI.create(endpoint))
.region(Region.of(region))
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package dev.barboud.taglens.controller;


import dev.barboud.taglens.dto.LoginDto;
import dev.barboud.taglens.dto.UserRegistrationDto;
import dev.barboud.taglens.dto.UserResponseDto;
import dev.barboud.taglens.dto.user.UserLoginDto;
import dev.barboud.taglens.dto.user.UserRegistrationDto;
import dev.barboud.taglens.dto.user.UserResponseDto;
import dev.barboud.taglens.service.AuthService;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
Expand Down Expand Up @@ -34,7 +34,7 @@ public ResponseEntity<Void> createUser(@Valid @RequestBody UserRegistrationDto r


@PostMapping("/login")
public ResponseEntity<Void> login(@Valid @RequestBody LoginDto loginDto) {
public ResponseEntity<Void> login(@Valid @RequestBody UserLoginDto loginDto) {
UUID sessionId = authService.login(loginDto);

ResponseCookie cookie = ResponseCookie.from("session_id", sessionId.toString())
Expand Down
47 changes: 47 additions & 0 deletions src/main/java/dev/barboud/taglens/controller/ImageController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package dev.barboud.taglens.controller;


import dev.barboud.taglens.dto.image.ImageResponseDto;
import dev.barboud.taglens.model.Image;
import dev.barboud.taglens.service.ImageService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.UUID;

@RestController
@RequestMapping("/images")
@RequiredArgsConstructor
public class ImageController {

private final ImageService imageService;


@PostMapping("/upload")
public ResponseEntity<ImageResponseDto> uploadImage(
@RequestParam("file") MultipartFile file,
@CookieValue("session_id") UUID sessionId
) throws Exception {

ImageResponseDto imageResponseDto = imageService.uploadImage(file, sessionId);

return ResponseEntity
.status(HttpStatus.CREATED)
.body(imageResponseDto);
}

// @GetMapping("/link")
// public ResponseEntity<Map<String, String>> getImageLink(@RequestParam String key) {
// return ResponseEntity.ok(Map.of("url", imageService.getImageLink(key, 15)));
// }
//
// @DeleteMapping
// public ResponseEntity<Void> deleteImage(@RequestParam String key) {
// imageService.deleteImage(key);
// return ResponseEntity.noContent().build();
// }

}
15 changes: 15 additions & 0 deletions src/main/java/dev/barboud/taglens/dto/image/ImageResponseDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package dev.barboud.taglens.dto.image;

import lombok.AllArgsConstructor;
import lombok.Data;

import java.util.UUID;

@Data
@AllArgsConstructor
public class ImageResponseDto {
UUID id;
String contentType;
Long fileSize;
private String storageKey;
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package dev.barboud.taglens.dto;
package dev.barboud.taglens.dto.user;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;

@Data
public class LoginDto {
public class UserLoginDto {

@NotBlank(message = "Email is required")
@Email(message = "Invalid email format")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package dev.barboud.taglens.dto;
package dev.barboud.taglens.dto.user;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package dev.barboud.taglens.dto;
package dev.barboud.taglens.dto.user;

import lombok.AllArgsConstructor;
import lombok.Data;
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/dev/barboud/taglens/model/Image.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package dev.barboud.taglens.model;

import lombok.Data;

import java.time.Instant;
import java.util.UUID;

@Data
public class Image {
private UUID id;
private UUID userId;
private String storageKey;
private String contentType;
private Long fileSize;
private Instant createdAt;
}
29 changes: 29 additions & 0 deletions src/main/java/dev/barboud/taglens/repository/ImageRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package dev.barboud.taglens.repository;

import dev.barboud.taglens.model.Image;
import lombok.RequiredArgsConstructor;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
@RequiredArgsConstructor
public class ImageRepository {

private final JdbcTemplate jdbcTemplate;

public void saveImage(Image image) {

jdbcTemplate.update("""
INSERT INTO images
(id, user_id, storage_key, content_type, file_size)
VALUES (?, ?, ?, ?, ?)
""",

image.getId(),
image.getUserId(),
image.getStorageKey(),
image.getContentType(),
image.getFileSize()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
@Repository
@RequiredArgsConstructor
public class SessionRepository {

private final JdbcTemplate jdbcTemplate;

public void createSession(Session session) {
Expand All @@ -22,7 +23,6 @@ public void createSession(Session session) {
);
}


public Optional<Session> findValidSession(UUID sessionId) {
String sql = "SELECT id, user_id, expires_at FROM sessions WHERE id = ? AND expires_at > NOW()";
List<Session> results = jdbcTemplate.query(sql, (rs, rowNum) -> {
Expand All @@ -36,7 +36,6 @@ public Optional<Session> findValidSession(UUID sessionId) {
return results.stream().findFirst();
}


public void deleteSession(UUID sessionId) {
jdbcTemplate.update("DELETE FROM sessions WHERE id = ?", sessionId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
@Repository
@RequiredArgsConstructor
public class UserRepository {
private final JdbcTemplate jdbcTemplate;

private final JdbcTemplate jdbcTemplate;

public void registerUser(User user) {
jdbcTemplate.update(
Expand All @@ -22,7 +22,6 @@ public void registerUser(User user) {
);
}


public Optional<User> findUserByEmail(String email) {
String sql = "SELECT id, email, password_hash FROM users WHERE email = ?";
List<User> results = jdbcTemplate.query(sql, (rs, rowNum) -> {
Expand All @@ -36,8 +35,6 @@ public Optional<User> findUserByEmail(String email) {
return results.stream().findFirst();
}



public Optional<User> findUserById(UUID id) {
String sql = "SELECT id, email, password_hash, created_at FROM users WHERE id = ?";
List<User> results = jdbcTemplate.query(sql, (rs, rowNum) -> {
Expand Down
8 changes: 4 additions & 4 deletions src/main/java/dev/barboud/taglens/service/AuthService.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package dev.barboud.taglens.service;

import dev.barboud.taglens.dto.LoginDto;
import dev.barboud.taglens.dto.UserRegistrationDto;
import dev.barboud.taglens.dto.UserResponseDto;
import dev.barboud.taglens.dto.user.UserLoginDto;
import dev.barboud.taglens.dto.user.UserRegistrationDto;
import dev.barboud.taglens.dto.user.UserResponseDto;
import dev.barboud.taglens.model.Session;
import dev.barboud.taglens.model.User;
import dev.barboud.taglens.repository.SessionRepository;
Expand Down Expand Up @@ -41,7 +41,7 @@ public void registerUser(UserRegistrationDto registrationDto) {
}


public UUID login(LoginDto loginDto) {
public UUID login(UserLoginDto loginDto) {
User user = userRepository.findUserByEmail(loginDto.getEmail())
.orElseThrow(() -> new BadCredentialsException("Invalid email or password"));

Expand Down
Loading
Loading