Compare commits
47 Commits
feature-is
...
feature-qr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20c9473bd3 | ||
|
|
31dbb35d00 | ||
|
|
a95e5e8fcd | ||
|
|
8cb63972a7 | ||
|
|
3ee3462fb5 | ||
|
|
8ca90d1507 | ||
|
|
430ed300e8 | ||
|
|
da12ec70c6 | ||
|
|
d29dddd133 | ||
|
|
7cb68e2ec0 | ||
|
|
77541c2a9d | ||
|
|
ee3180dbea | ||
|
|
f7e592f8ec | ||
|
|
a236eb13f8 | ||
|
|
0af328977b | ||
|
|
2771ac4f73 | ||
|
|
b966597ca6 | ||
|
|
686c4f939e | ||
|
|
3e0ad7db2d | ||
|
|
f65cee37f9 | ||
|
|
a02eaab0b1 | ||
|
|
d1dd29fa1d | ||
|
|
a08285ff43 | ||
|
|
f293353d36 | ||
|
|
1163648655 | ||
|
|
d273a2a085 | ||
|
|
8dad16f45a | ||
|
|
8f570a8605 | ||
|
|
41b02824cd | ||
|
|
7627c676fc | ||
|
|
e301b76dbf | ||
|
|
88bbcefc60 | ||
|
|
98878713d2 | ||
|
|
a8aa39b92f | ||
|
|
937bbaf3a7 | ||
|
|
4a3f389669 | ||
|
|
00a9b807bd | ||
|
|
c79ced4259 | ||
|
|
68b6a08310 | ||
|
|
23b0cea98b | ||
|
|
958f039a83 | ||
|
|
2c889f86f0 | ||
|
|
74ffd6885c | ||
|
|
36657ce742 | ||
|
|
be27afc248 | ||
|
|
e8fc84ba1c | ||
|
|
59cf2cdc3a |
4
.github/workflows/workflow.yml
vendored
4
.github/workflows/workflow.yml
vendored
@@ -37,6 +37,10 @@ jobs:
|
||||
echo "SERVER_SSL_KEY_ALIAS=${{ secrets.SERVER_SSL_KEY_ALIAS }}" >> $GITHUB_ENV
|
||||
echo "SERVER_SSL_TRUST_STORE_LOCATION=${{ secrets.SERVER_SSL_TRUST_STORE_LOCATION }}" >> $GITHUB_ENV
|
||||
echo "SERVER_SSL_TRUST_STORE_PASSWORD=${{ secrets.SERVER_SSL_TRUST_STORE_PASSWORD }}" >> $GITHUB_ENV
|
||||
echo "GOOGLE_SAFE_BROWSING_API_KEY=${{ secrets.GOOGLE_SAFE_BROWSING_API_KEY }}" >> $GITHUB_ENV
|
||||
echo "VIRUSTOTAL_API_KEY=${{ secrets.VIRUSTOTAL_API_KEY }}" >> $GITHUB_ENV
|
||||
echo "GOOGLE_CLIENT_ID=${{ secrets.GOOGLE_CLIENT_ID }}" >> $GITHUB_ENV
|
||||
echo "GOOGLE_CLIENT_SECRET=${{ secrets.GOOGLE_CLIENT_SECRET }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Build with Maven
|
||||
run: mvn -B package --file pom.xml
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -4,6 +4,9 @@ target/
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
.DS_Store
|
||||
*/.DS_Store
|
||||
**/.DS_Store
|
||||
credentials.json
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
@@ -36,3 +39,5 @@ build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
|
||||
|
||||
53
pom.xml
53
pom.xml
@@ -57,18 +57,59 @@
|
||||
<artifactId>reactor-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Reactor Core -->
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Netty DNS resolver for MacOS -->
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-resolver-dns-native-macos</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.hypersistence</groupId>
|
||||
<artifactId>hypersistence-utils-hibernate-63</artifactId>
|
||||
<version>3.8.1</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.google.api-client/google-api-client -->
|
||||
<dependency>
|
||||
<groupId>com.google.api-client</groupId>
|
||||
<artifactId>google-api-client</artifactId>
|
||||
<version>2.6.0</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.google.oauth-client/google-oauth-client-jetty -->
|
||||
<dependency>
|
||||
<groupId>com.google.oauth-client</groupId>
|
||||
<artifactId>google-oauth-client-jetty</artifactId>
|
||||
<version>1.36.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.apis</groupId>
|
||||
<artifactId>google-api-services-gmail</artifactId>
|
||||
<version>v1-rev20240520-2.0.0</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.json/json -->
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20240303</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.google.zxing/core -->
|
||||
<dependency>
|
||||
<groupId>com.google.zxing</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>3.5.3</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.google.zxing/javase -->
|
||||
<dependency>
|
||||
<groupId>com.google.zxing</groupId>
|
||||
<artifactId>javase</artifactId>
|
||||
<version>3.5.3</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
<version>1.18.1</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
BIN
src/.DS_Store
vendored
BIN
src/.DS_Store
vendored
Binary file not shown.
BIN
src/main/.DS_Store
vendored
BIN
src/main/.DS_Store
vendored
Binary file not shown.
BIN
src/main/java/.DS_Store
vendored
BIN
src/main/java/.DS_Store
vendored
Binary file not shown.
BIN
src/main/java/com/.DS_Store
vendored
BIN
src/main/java/com/.DS_Store
vendored
Binary file not shown.
BIN
src/main/java/com/safeqr/.DS_Store
vendored
BIN
src/main/java/com/safeqr/.DS_Store
vendored
Binary file not shown.
BIN
src/main/java/com/safeqr/app/.DS_Store
vendored
BIN
src/main/java/com/safeqr/app/.DS_Store
vendored
Binary file not shown.
27
src/main/java/com/safeqr/app/constants/APIConstants.java
Normal file
27
src/main/java/com/safeqr/app/constants/APIConstants.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package com.safeqr.app.constants;
|
||||
|
||||
public class APIConstants {
|
||||
private APIConstants() {
|
||||
//private constructor to prevent instantiation
|
||||
}
|
||||
public static final String APPLICATION_NAME = "SafeQR";
|
||||
public static final String API_VERSION = "v1";
|
||||
public static final String API_URL_QRCODE_GET_ALL = "/qrcodetypes";
|
||||
public static final String API_URL_QRCODE_SCAN = "/qrcodetypes/scan";
|
||||
public static final String API_URL_QRCODE_DETECT = "/qrcodetypes/detect";
|
||||
public static final String API_URL_QRCODE_VERIFY_URL = "/qrcodetypes/verifyURL";
|
||||
public static final String API_URL_QRCODE_VIRUS_TOTAL_CHECK = "/qrcodetypes/virusTotalCheck";
|
||||
public static final String API_URL_QRCODE_REDIRECT_COUNT = "/qrcodetypes/checkRedirects";
|
||||
public static final String API_URL_QRCODE_GET_QR_DETAILS = "/qrcodetypes/getQRDetails";
|
||||
|
||||
|
||||
public static final String API_URL_USER_GET = "/user/getUser";
|
||||
public static final String API_URL_USER_GET_SCANNED_HISTORIES = "/user/getScannedHistories";
|
||||
public static final String API_URL_USER_DELETE_SCANNED_HISTORIES = "/user/deleteScannedHistories";
|
||||
public static final String API_URL_USER_DELETE_ALL_SCANNED_HISTORIES = "/user/deleteAllScannedHistories";
|
||||
public static final String API_URL_USER_GET_BOOKMARKS = "/user/getBookmarks";
|
||||
public static final String API_URL_USER_SET_BOOKMARK = "/user/setBookmark";
|
||||
public static final String API_URL_USER_DELETE_BOOKMARK = "/user/deleteBookmark";
|
||||
public static final String API_URL_USER_DELETE_ALL_BOOKMARK = "/user/deleteAllBookmark";
|
||||
|
||||
}
|
||||
20
src/main/java/com/safeqr/app/constants/CommonConstants.java
Normal file
20
src/main/java/com/safeqr/app/constants/CommonConstants.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package com.safeqr.app.constants;
|
||||
|
||||
public class CommonConstants {
|
||||
private CommonConstants() {
|
||||
//private constructor to prevent instantiation
|
||||
}
|
||||
public static final String HEADER_USER_ID = "X-USER-ID";
|
||||
public static final String DEFAULT_QR_CODE_TYPE = "TEXT";
|
||||
public static final int MAX_REDIRECT_COUNT = 20;
|
||||
public static final String QR_CODE_TYPE_EMAIL = "EMAIL";
|
||||
public static final String QR_CODE_TYPE_PHONE = "PHONE";
|
||||
public static final String QR_CODE_TYPE_SMS = "SMS";
|
||||
public static final String QR_CODE_TYPE_URL = "URL";
|
||||
public static final String QR_CODE_TYPE_WIFI = "WIFI";
|
||||
|
||||
public static final String INFO_NON_SECURE_CONNECTION = "Not an HTTPS connection";
|
||||
public static final String INFO_NO_HSTS_HEADER = "No HSTS Header detected";
|
||||
public static final String INFO_HSTS_HEADER_PREFIX = "HSTS Header: ";
|
||||
public static final String INFO_HSTS_NOT_APPLICABLE = "N/A";
|
||||
}
|
||||
16
src/main/java/com/safeqr/app/exceptions/ErrorResponse.java
Normal file
16
src/main/java/com/safeqr/app/exceptions/ErrorResponse.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package com.safeqr.app.exceptions;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@Builder
|
||||
@Data
|
||||
public class ErrorResponse {
|
||||
private String error;
|
||||
private int status;
|
||||
|
||||
public ErrorResponse(String message, int status){
|
||||
this.error = message;
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.safeqr.app.exceptions;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
|
||||
@ControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
@ExceptionHandler(ResourceNotFoundExceptions.class)
|
||||
public ResponseEntity<ErrorResponse> handleResourceNotFoundException(ResourceNotFoundExceptions e) {
|
||||
return new ResponseEntity<>(new ErrorResponse(e.getMessage(), HttpStatus.NOT_FOUND.value()), HttpStatus.NOT_FOUND);
|
||||
}
|
||||
@ExceptionHandler(ResourceAlreadyExists.class)
|
||||
public ResponseEntity<ErrorResponse> handleResourceAlreadyExistsException(ResourceAlreadyExists e) {
|
||||
return new ResponseEntity<>(new ErrorResponse(e.getMessage(), HttpStatus.BAD_REQUEST.value()), HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.safeqr.app.exceptions;
|
||||
|
||||
public class ResourceAlreadyExists extends RuntimeException {
|
||||
public ResourceAlreadyExists(String message){
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.safeqr.app.exceptions;
|
||||
|
||||
public class ResourceNotFoundExceptions extends RuntimeException {
|
||||
public ResourceNotFoundExceptions(String message){
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.safeqr.app.gmail.controller;
|
||||
|
||||
import com.google.api.services.gmail.model.*;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.json.JSONObject;
|
||||
import com.google.api.client.auth.oauth2.AuthorizationCodeRequestUrl;
|
||||
import com.google.api.client.auth.oauth2.Credential;
|
||||
import com.google.api.client.auth.oauth2.TokenResponse;
|
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
|
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
|
||||
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
|
||||
import com.google.api.client.http.HttpTransport;
|
||||
import com.google.api.client.json.JsonFactory;
|
||||
import com.google.api.client.json.gson.GsonFactory;
|
||||
import com.google.api.services.gmail.GmailScopes;
|
||||
import com.safeqr.app.gmail.service.GmailService;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
import static com.safeqr.app.constants.APIConstants.*;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
|
||||
@RestController
|
||||
@RequestMapping(API_VERSION)
|
||||
public class GmailController {
|
||||
private static final Logger logger = LoggerFactory.getLogger(GmailController.class);
|
||||
GmailService gmailService;
|
||||
private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();
|
||||
private static com.google.api.services.gmail.Gmail client;
|
||||
|
||||
GoogleClientSecrets clientSecrets;
|
||||
GoogleAuthorizationCodeFlow flow;
|
||||
Credential credential;
|
||||
|
||||
@Value("${gmail.client.clientId}")
|
||||
private String clientId;
|
||||
|
||||
@Value("${gmail.client.clientSecret}")
|
||||
private String clientSecret;
|
||||
|
||||
@Value("${gmail.client.redirectUri}")
|
||||
private String redirectUri;
|
||||
|
||||
@Autowired
|
||||
public GmailController(GmailService gmailService) {
|
||||
this.gmailService = gmailService;
|
||||
}
|
||||
|
||||
@GetMapping(value = "/gmail/login")
|
||||
public RedirectView googleConnectionStatus(HttpServletRequest request) throws Exception {
|
||||
return new RedirectView(authorize());
|
||||
}
|
||||
private String authorize() throws Exception {
|
||||
|
||||
AuthorizationCodeRequestUrl authorizationUrl;
|
||||
if (flow == null) {
|
||||
GoogleClientSecrets.Details web = new GoogleClientSecrets.Details();
|
||||
web.setClientId(clientId);
|
||||
web.setClientSecret(clientSecret);
|
||||
clientSecrets = new GoogleClientSecrets().setWeb(web);
|
||||
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
|
||||
flow = new GoogleAuthorizationCodeFlow.Builder(httpTransport, JSON_FACTORY, clientSecrets,
|
||||
Collections.singleton(GmailScopes.GMAIL_READONLY))
|
||||
.build();
|
||||
}
|
||||
authorizationUrl = flow.newAuthorizationUrl()
|
||||
.setRedirectUri(redirectUri)
|
||||
.setAccessType("offline")
|
||||
//.setApprovalPrompt("force") // force refresh token
|
||||
;
|
||||
|
||||
logger.info("gmail authorizationUrl -> {}", authorizationUrl);
|
||||
return authorizationUrl.build();
|
||||
}
|
||||
|
||||
@GetMapping(value = "/gmail/callback", params = "code")
|
||||
public ResponseEntity<String> oauth2Callback(@RequestParam(value = "code") String code) {
|
||||
JSONObject json = new JSONObject();
|
||||
try {
|
||||
TokenResponse response = flow.newTokenRequest(code).setRedirectUri(redirectUri).execute();
|
||||
credential = flow.createAndStoreCredential(response, "userID");
|
||||
logger.info(credential.getAccessToken());
|
||||
logger.info(credential.getRefreshToken());
|
||||
logger.info(credential.toString());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return new ResponseEntity<>(json.toString(), HttpStatus.OK);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/gmail/getEmails", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<?> getUserEmails(@RequestHeader(name = "accessToken") String accessToken) throws IOException, InterruptedException {
|
||||
logger.info("Invoking GET Scan User Emails endpoints");
|
||||
if (accessToken == null || accessToken.isEmpty()) {
|
||||
return new ResponseEntity<>("Access token is missing", HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
return new ResponseEntity<>(gmailService.getEmail(accessToken).toString(), HttpStatus.OK);
|
||||
}
|
||||
}
|
||||
|
||||
242
src/main/java/com/safeqr/app/gmail/service/GmailService.java
Normal file
242
src/main/java/com/safeqr/app/gmail/service/GmailService.java
Normal file
@@ -0,0 +1,242 @@
|
||||
package com.safeqr.app.gmail.service;
|
||||
|
||||
import com.google.api.client.auth.oauth2.BearerToken;
|
||||
import com.google.api.client.http.HttpTransport;
|
||||
import com.google.api.services.gmail.model.*;
|
||||
import com.google.zxing.*;
|
||||
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
|
||||
import com.google.zxing.common.HybridBinarizer;
|
||||
import com.google.zxing.multi.qrcode.QRCodeMultiReader;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.select.Elements;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.google.api.client.auth.oauth2.Credential;
|
||||
import com.google.api.client.http.javanet.NetHttpTransport;
|
||||
import com.google.api.client.json.JsonFactory;
|
||||
import com.google.api.client.json.gson.GsonFactory;
|
||||
import com.google.api.services.gmail.Gmail;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.safeqr.app.constants.APIConstants.APPLICATION_NAME;
|
||||
|
||||
@Service
|
||||
public class GmailService {
|
||||
private static final Logger logger = LoggerFactory.getLogger(GmailService.class);
|
||||
private static final HttpTransport httpTransport = new NetHttpTransport();
|
||||
private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();
|
||||
|
||||
private Gmail getGmailService(String accessToken) {
|
||||
Credential userCredentials = new Credential(BearerToken.authorizationHeaderAccessMethod()).setAccessToken(accessToken);
|
||||
return new Gmail.Builder(httpTransport, JSON_FACTORY, userCredentials)
|
||||
.setApplicationName(APPLICATION_NAME)
|
||||
.build();
|
||||
}
|
||||
|
||||
public JSONObject getEmail(String accessToken) throws IOException, InterruptedException {
|
||||
JSONObject json = new JSONObject();
|
||||
JSONArray emailArray = new JSONArray();
|
||||
|
||||
// Build the Gmail service
|
||||
Gmail service = getGmailService(accessToken);
|
||||
logger.info("service-> {}", service);
|
||||
|
||||
// Get the list of messages
|
||||
ListMessagesResponse listResponse = service.users().messages().list("me").execute();
|
||||
List<Message> messages = listResponse.getMessages();
|
||||
|
||||
for (Message message : messages) {
|
||||
message = service.users().messages().get("me", message.getId()).setFormat("full").execute();
|
||||
List<MessagePart> parts = message.getPayload().getParts();
|
||||
Set<String> attachmentIds = new HashSet<>();
|
||||
Set<String> imageUrls = new HashSet<>();
|
||||
processPartsRecursively(parts, attachmentIds, imageUrls);
|
||||
|
||||
// Extract and log the email subject
|
||||
String subject = getSubject(message);
|
||||
logger.info("Email Subject-> {}", subject);
|
||||
|
||||
if (attachmentIds.isEmpty() && imageUrls.isEmpty())
|
||||
continue;
|
||||
|
||||
String messageId = message.getId();
|
||||
logger.info("messageId-> {}", messageId);
|
||||
String historyId = String.valueOf(message.getHistoryId());
|
||||
logger.info("historyId-> {}", historyId);
|
||||
|
||||
for (String attachmentId : attachmentIds) {
|
||||
Optional<String> attachment = findAttachmentIdByCid(parts, attachmentId);
|
||||
logger.info("attachment-> {}", attachment);
|
||||
if (attachment.isPresent()) {
|
||||
List<String> qrCodeValue = processAttachment(service, messageId, attachment.get());
|
||||
emailArray.put(qrCodeValue);
|
||||
}
|
||||
}
|
||||
for (String imageUrl : imageUrls) {
|
||||
List<String> qrCodeValue = scanQRCodeFromUrl(imageUrl);
|
||||
if (qrCodeValue != null) {
|
||||
emailArray.put(qrCodeValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.info("Total Emails-> {}", messages.size());
|
||||
json.put("qr_codes", emailArray);
|
||||
return json;
|
||||
}
|
||||
|
||||
private String getSubject(Message message) {
|
||||
return message.getPayload().getHeaders().stream()
|
||||
.filter(header -> "Subject".equals(header.getName()))
|
||||
.findFirst()
|
||||
.map(MessagePartHeader::getValue)
|
||||
.orElse("No Subject");
|
||||
}
|
||||
private Optional<String> findAttachmentIdByCid(List<MessagePart> parts, String cid) {
|
||||
return parts.stream()
|
||||
.flatMap(part -> Stream.concat(findAttachmentIdInCurrentPart(part, cid).stream(), Optional.ofNullable(part.getParts())
|
||||
.flatMap(subParts -> findAttachmentIdByCid(subParts, cid)).stream()))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
private Optional<String> findAttachmentIdInCurrentPart(MessagePart part, String cid) {
|
||||
return Optional.ofNullable(part.getHeaders())
|
||||
.flatMap(headers -> headers.stream()
|
||||
.filter(header -> isContentIdHeader(header, cid))
|
||||
.findFirst()
|
||||
.map(header -> part.getBody().getAttachmentId()));
|
||||
}
|
||||
|
||||
private boolean isContentIdHeader(MessagePartHeader header, String cid) {
|
||||
return "Content-ID".equalsIgnoreCase(header.getName()) && header.getValue().contains(cid);
|
||||
}
|
||||
// Recursive method to handle nested parts to search for CID URIs
|
||||
private void processPartsRecursively(List<MessagePart> parts, Set<String> attachmentIds, Set<String> imageURLs) {
|
||||
if (parts != null) {
|
||||
for (MessagePart part : parts) {
|
||||
if (part.getMimeType().equalsIgnoreCase("text/html")) {
|
||||
String html = new String(Base64.decodeBase64(part.getBody().getData()));
|
||||
attachmentIds.addAll(extractCIDsFromHtml(html));
|
||||
imageURLs.addAll(extractImageUrlsFromHtml(html));
|
||||
} else if (part.getParts() != null) {
|
||||
// Recursive call to handle nested parts
|
||||
processPartsRecursively(part.getParts(), attachmentIds, imageURLs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private List<String> scanQRCodeFromUrl(String imageUrl) throws IOException, InterruptedException {
|
||||
try {
|
||||
BufferedImage image = downloadImageFromUrl(imageUrl);
|
||||
if (image != null) {
|
||||
return decodeQRCodes(image);
|
||||
}
|
||||
} catch(URISyntaxException e) {
|
||||
logger.error("Error while scanning QR code from URL", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// Download the image from the given URL
|
||||
private BufferedImage downloadImageFromUrl(String imageUrl) throws IOException, InterruptedException, URISyntaxException {
|
||||
HttpClient client = HttpClient.newBuilder()
|
||||
.followRedirects(HttpClient.Redirect.ALWAYS)
|
||||
.build();
|
||||
logger.info("imageUrl-> {}", imageUrl);
|
||||
// Encode the URL
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(imageUrl.replace(" ", "%20")))
|
||||
.GET()
|
||||
.build();
|
||||
|
||||
HttpResponse<byte[]> response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
|
||||
if (response.statusCode() == 200) {
|
||||
byte[] imageBytes = response.body();
|
||||
return ImageIO.read(new ByteArrayInputStream(imageBytes));
|
||||
} else {
|
||||
logger.error("Failed to download image. HTTP response code: {}", response.statusCode());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private List<String> processAttachment(Gmail service, String messageId, String attachmentId) throws IOException {
|
||||
MessagePartBody attachPart = service.users().messages().attachments().get("me", messageId, attachmentId).execute();
|
||||
byte[] imageBytes = Base64.decodeBase64(attachPart.getData());
|
||||
BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageBytes));
|
||||
// ImageIO.write(image, "png", new File("debug_image.png"));
|
||||
return decodeQRCodes(image);
|
||||
}
|
||||
|
||||
private List<String> decodeQRCodes(BufferedImage image) {
|
||||
List<String> qrCodeValues = new ArrayList<>();
|
||||
LuminanceSource source = new BufferedImageLuminanceSource(image);
|
||||
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
|
||||
|
||||
// Set up decoding hints
|
||||
Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);
|
||||
hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
|
||||
hints.put(DecodeHintType.POSSIBLE_FORMATS, List.of(BarcodeFormat.QR_CODE));
|
||||
|
||||
try {
|
||||
QRCodeMultiReader multiReader = new QRCodeMultiReader();
|
||||
Result[] results = multiReader.decodeMultiple(bitmap, hints);
|
||||
|
||||
if (results != null) {
|
||||
for (Result result : results) {
|
||||
qrCodeValues.add(result.getText());
|
||||
logger.info("Detected QR code: {}", result.getText());
|
||||
}
|
||||
} else {
|
||||
logger.info("No QR codes found in the image");
|
||||
}
|
||||
} catch (NotFoundException e) {
|
||||
logger.info("No QR codes found in the image");
|
||||
} catch (Exception e) {
|
||||
logger.error("Error decoding QR codes", e);
|
||||
}
|
||||
|
||||
logger.info("Total QR codes found: {}", qrCodeValues.size());
|
||||
return qrCodeValues;
|
||||
}
|
||||
|
||||
//Extract CIDs from HTML
|
||||
private Set<String> extractCIDsFromHtml(String html) {
|
||||
Document doc = Jsoup.parse(html);
|
||||
Elements imgs = doc.select("img[src^=cid:]");
|
||||
|
||||
return imgs.stream()
|
||||
.map(img -> img.attr("src"))
|
||||
.filter(src -> src.startsWith("cid:"))
|
||||
.map(src -> src.substring(4)) // Remove "cid:" prefix
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
//Extract image URLs from HTML
|
||||
private Set<String> extractImageUrlsFromHtml(String html) {
|
||||
Document doc = Jsoup.parse(html);
|
||||
Elements imgs = doc.select("img[src]");
|
||||
|
||||
return imgs.stream()
|
||||
.map(img -> img.attr("src"))
|
||||
.filter(this::isImageUrl)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
// Check if the URL is an image URL
|
||||
private boolean isImageUrl(String url) {
|
||||
String lowerUrl = url.toLowerCase();
|
||||
return lowerUrl.endsWith(".jpg") || lowerUrl.endsWith(".jpeg") || lowerUrl.endsWith(".png") || lowerUrl.endsWith(".gif") || lowerUrl.endsWith(".bmp");
|
||||
}
|
||||
|
||||
}
|
||||
BIN
src/main/java/com/safeqr/app/qrcode/.DS_Store
vendored
BIN
src/main/java/com/safeqr/app/qrcode/.DS_Store
vendored
Binary file not shown.
@@ -1,22 +1,31 @@
|
||||
package com.safeqr.app.qrcode.controller;
|
||||
|
||||
import com.safeqr.app.qrcode.dto.QRCodePayload;
|
||||
import static com.safeqr.app.constants.APIConstants.*;
|
||||
import static com.safeqr.app.constants.CommonConstants.*;
|
||||
import com.safeqr.app.qrcode.dto.request.QRCodePayload;
|
||||
import com.safeqr.app.qrcode.dto.RedirectCountResponse;
|
||||
import com.safeqr.app.qrcode.dto.URLVerificationResponse;
|
||||
import com.safeqr.app.qrcode.entity.QRCodeType;
|
||||
import com.safeqr.app.qrcode.dto.response.BaseScanResponse;
|
||||
import com.safeqr.app.qrcode.entity.QRCodeTypeEntity;
|
||||
import com.safeqr.app.qrcode.service.QRCodeTypeService;
|
||||
import com.safeqr.app.qrcode.service.RedirectCountService;
|
||||
import com.safeqr.app.qrcode.service.URLVerificationService;
|
||||
import com.safeqr.app.qrcode.service.VirusTotalService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/v1/api/qrcodetypes")
|
||||
@RequestMapping(API_VERSION)
|
||||
public class QRCodeTypeController {
|
||||
private static final Logger logger = LoggerFactory.getLogger(QRCodeTypeController.class);
|
||||
|
||||
@Autowired
|
||||
private QRCodeTypeService qrCodeTypeService;
|
||||
@@ -30,23 +39,35 @@ public class QRCodeTypeController {
|
||||
@Autowired
|
||||
private RedirectCountService redirectCountService;
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<List<QRCodeType>> getAllTypes() {
|
||||
@GetMapping(value = API_URL_QRCODE_GET_ALL)
|
||||
public ResponseEntity<List<QRCodeTypeEntity>> getAllTypes() {
|
||||
return ResponseEntity.ok(qrCodeTypeService.getAllTypes());
|
||||
}
|
||||
@GetMapping(value = API_URL_QRCODE_GET_QR_DETAILS)
|
||||
public ResponseEntity<BaseScanResponse> getScannedQRCodeDetails(@RequestHeader(name="QR-ID") UUID qrCodeId) {
|
||||
logger.info("Invoking GET QRCode details endpoint, qrCodeId: {}", qrCodeId);
|
||||
return ResponseEntity.ok(qrCodeTypeService.getScannedQRCodeDetails(qrCodeId));
|
||||
}
|
||||
|
||||
@PostMapping("/detect")
|
||||
@PostMapping(value = API_URL_QRCODE_SCAN, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<BaseScanResponse> scanQRCode(@RequestBody QRCodePayload payload,
|
||||
@RequestHeader(required = false, name = HEADER_USER_ID) String userId) {
|
||||
logger.info("Invoking scan endpoint");
|
||||
return ResponseEntity.ok(qrCodeTypeService.scanQRCode(userId, payload));
|
||||
}
|
||||
|
||||
@PostMapping(API_URL_QRCODE_DETECT)
|
||||
public ResponseEntity<String> detectType(@RequestBody QRCodePayload payload) {
|
||||
return ResponseEntity.ok(qrCodeTypeService.detectType(payload).block());
|
||||
}
|
||||
|
||||
@PostMapping("/verifyURL")
|
||||
@PostMapping(API_URL_QRCODE_VERIFY_URL)
|
||||
public ResponseEntity<URLVerificationResponse> verifyURL(@RequestBody QRCodePayload payload) {
|
||||
URLVerificationResponse response = urlVerificationService.verifyURL(payload);
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
@PostMapping("/virusTotalCheck")
|
||||
@PostMapping(API_URL_QRCODE_VIRUS_TOTAL_CHECK)
|
||||
public ResponseEntity<Boolean> virusTotalCheck(@RequestBody QRCodePayload payload) {
|
||||
try {
|
||||
String analysisId = virusTotalService.scanURL(payload);
|
||||
@@ -57,7 +78,7 @@ public class QRCodeTypeController {
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/checkRedirects")
|
||||
@PostMapping(API_URL_QRCODE_REDIRECT_COUNT)
|
||||
public ResponseEntity<RedirectCountResponse> checkRedirects(@RequestBody QRCodePayload payload) {
|
||||
return ResponseEntity.ok(redirectCountService.countRedirects(payload).block());
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.safeqr.app.qrcode.dto;
|
||||
package com.safeqr.app.qrcode.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.safeqr.app.qrcode.dto.response;
|
||||
|
||||
import com.safeqr.app.qrcode.model.QRCodeModel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class BaseScanResponse {
|
||||
private QRCodeModel qrcode;
|
||||
}
|
||||
35
src/main/java/com/safeqr/app/qrcode/entity/EmailEntity.java
Normal file
35
src/main/java/com/safeqr/app/qrcode/entity/EmailEntity.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.safeqr.app.qrcode.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Builder;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.annotations.UuidGenerator;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "email", schema = "safeqr")
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class EmailEntity {
|
||||
@Id
|
||||
@JsonIgnore
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
@UuidGenerator
|
||||
@Column(updatable = false, nullable = false)
|
||||
private UUID id;
|
||||
|
||||
@JsonIgnore
|
||||
@Column(name = "qr_code_id")
|
||||
private UUID qrCodeId;
|
||||
|
||||
private String email;
|
||||
private String title;
|
||||
private String message;
|
||||
}
|
||||
33
src/main/java/com/safeqr/app/qrcode/entity/PhoneEntity.java
Normal file
33
src/main/java/com/safeqr/app/qrcode/entity/PhoneEntity.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package com.safeqr.app.qrcode.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Builder;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.annotations.UuidGenerator;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "phone", schema = "safeqr")
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PhoneEntity {
|
||||
@Id
|
||||
@JsonIgnore
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
@UuidGenerator
|
||||
@Column(updatable = false, nullable = false)
|
||||
private UUID id;
|
||||
|
||||
@JsonIgnore
|
||||
@Column(name = "qr_code_id")
|
||||
private UUID qrCodeId;
|
||||
|
||||
private String phone;
|
||||
}
|
||||
@@ -1,30 +1,39 @@
|
||||
|
||||
package com.safeqr.app.qrcode.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.annotations.UuidGenerator;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "qr_code", schema = "safeqr")
|
||||
@Data
|
||||
public class QRCode {
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class QRCodeEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(generator = "UUID")
|
||||
@GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
|
||||
@UuidGenerator
|
||||
@Column(updatable = false, nullable = false)
|
||||
private UUID id;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "qr_code_type_id", nullable = false)
|
||||
private QRCodeType qrCodeType;
|
||||
|
||||
@JsonIgnore
|
||||
private String userId;
|
||||
private String contents;
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "qr_code_type_id", nullable = false)
|
||||
private QRCodeTypeEntity info;
|
||||
|
||||
@Column(name = "created_at", insertable = false, updatable = false)
|
||||
private String createdAt;
|
||||
private LocalDateTime createdAt;
|
||||
}
|
||||
@@ -1,20 +1,25 @@
|
||||
|
||||
package com.safeqr.app.qrcode.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
|
||||
@Entity
|
||||
@Table(name = "qr_code_types", schema = "safeqr")
|
||||
@Data
|
||||
public class QRCodeType {
|
||||
public class QRCodeTypeEntity {
|
||||
|
||||
@Id
|
||||
@JsonIgnore
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String type;
|
||||
private String description;
|
||||
|
||||
@JsonIgnore
|
||||
private String prefix;
|
||||
|
||||
@JsonIgnore
|
||||
private String tableName;
|
||||
}
|
||||
34
src/main/java/com/safeqr/app/qrcode/entity/SMSEntity.java
Normal file
34
src/main/java/com/safeqr/app/qrcode/entity/SMSEntity.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package com.safeqr.app.qrcode.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Builder;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.annotations.UuidGenerator;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "sms", schema = "safeqr")
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SMSEntity {
|
||||
@Id
|
||||
@JsonIgnore
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
@UuidGenerator
|
||||
@Column(updatable = false, nullable = false)
|
||||
private UUID id;
|
||||
|
||||
@JsonIgnore
|
||||
@Column(name = "qr_code_id")
|
||||
private UUID qrCodeId;
|
||||
|
||||
private String phone;
|
||||
private String message;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.safeqr.app.qrcode.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Table(name = "scan_bookmark", schema = "safeqr")
|
||||
public class ScanBookmarkEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "qr_code_id", nullable = false)
|
||||
private UUID qrCodeId;
|
||||
|
||||
@Column(name = "user_id", nullable = false)
|
||||
private String userId;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "status")
|
||||
private BookmarkStatus bookmarkStatus;
|
||||
|
||||
@Column(name = "date_created", updatable = false)
|
||||
private OffsetDateTime dateCreated;
|
||||
|
||||
@Column(name = "date_updated")
|
||||
private OffsetDateTime dateUpdated;
|
||||
|
||||
public enum BookmarkStatus {
|
||||
ACTIVE,
|
||||
INACTIVE
|
||||
}
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "qr_code_id", referencedColumnName = "id", insertable = false, updatable = false)
|
||||
private QRCodeEntity qrCodeEntity;
|
||||
|
||||
@PrePersist
|
||||
public void prePersist() {
|
||||
OffsetDateTime now = OffsetDateTime.now();
|
||||
dateCreated = now;
|
||||
dateUpdated = now;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.safeqr.app.qrcode.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Table(name = "scan_history", schema = "safeqr")
|
||||
public class ScanHistoryEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "qr_code_id", nullable = false)
|
||||
private UUID qrCodeId;
|
||||
|
||||
@Column(name = "user_id", nullable = false)
|
||||
private String userId;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "status")
|
||||
private ScanStatus scanStatus;
|
||||
|
||||
@Column(name = "date_created", updatable = false)
|
||||
private OffsetDateTime dateCreated;
|
||||
|
||||
@Column(name = "date_updated")
|
||||
private OffsetDateTime dateUpdated;
|
||||
|
||||
@Column(name = "bookmarked")
|
||||
private boolean bookmarked;
|
||||
|
||||
public enum ScanStatus {
|
||||
ACTIVE,
|
||||
INACTIVE
|
||||
}
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "qr_code_id", referencedColumnName = "id", insertable = false, updatable = false)
|
||||
private QRCodeEntity qrCodeEntity;
|
||||
|
||||
@PrePersist
|
||||
public void prePersist() {
|
||||
OffsetDateTime now = OffsetDateTime.now();
|
||||
dateCreated = now;
|
||||
dateUpdated = now;
|
||||
}
|
||||
}
|
||||
33
src/main/java/com/safeqr/app/qrcode/entity/TextEntity.java
Normal file
33
src/main/java/com/safeqr/app/qrcode/entity/TextEntity.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package com.safeqr.app.qrcode.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Builder;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.annotations.UuidGenerator;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "text", schema = "safeqr")
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TextEntity {
|
||||
@Id
|
||||
@JsonIgnore
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
@UuidGenerator
|
||||
@Column(updatable = false, nullable = false)
|
||||
private UUID id;
|
||||
|
||||
@JsonIgnore
|
||||
@Column(name = "qr_code_id")
|
||||
private UUID qrCodeId;
|
||||
|
||||
private String text;
|
||||
}
|
||||
61
src/main/java/com/safeqr/app/qrcode/entity/URLEntity.java
Normal file
61
src/main/java/com/safeqr/app/qrcode/entity/URLEntity.java
Normal file
@@ -0,0 +1,61 @@
|
||||
package com.safeqr.app.qrcode.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.hypersistence.utils.hibernate.type.array.ListArrayType;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import org.hibernate.annotations.Type;
|
||||
import org.hibernate.annotations.UuidGenerator;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "url", schema = "safeqr")
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class URLEntity {
|
||||
@Id
|
||||
@JsonIgnore
|
||||
@GeneratedValue(generator = "UUID")
|
||||
@UuidGenerator
|
||||
@Column(updatable = false, nullable = false)
|
||||
private UUID id;
|
||||
|
||||
@JsonIgnore
|
||||
@Column(name = "qr_code_id")
|
||||
private UUID qrCodeId;
|
||||
|
||||
private String domain;
|
||||
|
||||
private String subdomain;
|
||||
|
||||
private String topLevelDomain;
|
||||
|
||||
private String path;
|
||||
|
||||
@JsonProperty
|
||||
private String query;
|
||||
|
||||
private String fragment;
|
||||
|
||||
private int redirect = 0;
|
||||
|
||||
@Type(ListArrayType.class)
|
||||
@Column(name = "hsts_header", columnDefinition = "text[]")
|
||||
private List<String> hstsHeader;
|
||||
|
||||
@Type(ListArrayType.class)
|
||||
@Column(name = "ssl_stripping", columnDefinition = "boolean[]")
|
||||
private List<Boolean> sslStripping;
|
||||
|
||||
@Type(ListArrayType.class)
|
||||
@Column(name = "redirect_chain", columnDefinition = "text[]")
|
||||
private List<String> redirectChain;
|
||||
}
|
||||
36
src/main/java/com/safeqr/app/qrcode/entity/WifiEntity.java
Normal file
36
src/main/java/com/safeqr/app/qrcode/entity/WifiEntity.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package com.safeqr.app.qrcode.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Builder;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.annotations.UuidGenerator;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "wifi", schema = "safeqr")
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class WifiEntity {
|
||||
@Id
|
||||
@JsonIgnore
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
@UuidGenerator
|
||||
@Column(updatable = false, nullable = false)
|
||||
private UUID id;
|
||||
|
||||
@JsonIgnore
|
||||
@Column(name = "qr_code_id")
|
||||
private UUID qrCodeId;
|
||||
|
||||
private String ssid;
|
||||
private String password;
|
||||
private String encryption;
|
||||
private boolean hidden;
|
||||
}
|
||||
38
src/main/java/com/safeqr/app/qrcode/model/EmailModel.java
Normal file
38
src/main/java/com/safeqr/app/qrcode/model/EmailModel.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package com.safeqr.app.qrcode.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.safeqr.app.qrcode.entity.QRCodeEntity;
|
||||
import com.safeqr.app.qrcode.entity.EmailEntity;
|
||||
|
||||
import com.safeqr.app.qrcode.service.EmailVerificationService;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public final class EmailModel extends QRCodeModel<EmailEntity> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(EmailModel.class);
|
||||
|
||||
@JsonIgnore
|
||||
private final EmailVerificationService emailVerificationService;
|
||||
|
||||
public EmailModel(QRCodeEntity scannedQRCodeEntity, EmailVerificationService emailVerificationService) {
|
||||
this.data = scannedQRCodeEntity;
|
||||
this.emailVerificationService = emailVerificationService;
|
||||
this.details = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDetails() {
|
||||
details = EmailEntity.builder().qrCodeId(data.getId()).build();
|
||||
// Insert into email table
|
||||
emailVerificationService.insertDB(details);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmailEntity getDetails () {
|
||||
return emailVerificationService.getEmailEntityByQRCodeId(data.getId());
|
||||
}
|
||||
}
|
||||
37
src/main/java/com/safeqr/app/qrcode/model/PhoneModel.java
Normal file
37
src/main/java/com/safeqr/app/qrcode/model/PhoneModel.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package com.safeqr.app.qrcode.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.safeqr.app.qrcode.entity.EmailEntity;
|
||||
import com.safeqr.app.qrcode.entity.QRCodeEntity;
|
||||
import com.safeqr.app.qrcode.entity.PhoneEntity;
|
||||
import com.safeqr.app.qrcode.service.PhoneVerificationService;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public final class PhoneModel extends QRCodeModel<PhoneEntity> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(PhoneModel.class);
|
||||
|
||||
@JsonIgnore
|
||||
private final PhoneVerificationService phoneVerificationService;
|
||||
|
||||
public PhoneModel(QRCodeEntity scannedQRCodeEntity, PhoneVerificationService phoneVerificationService) {
|
||||
this.data = scannedQRCodeEntity;
|
||||
this.phoneVerificationService = phoneVerificationService;
|
||||
this.details = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDetails() {
|
||||
details = PhoneEntity.builder().qrCodeId(data.getId()).build();
|
||||
// Insert into phone table
|
||||
phoneVerificationService.insertDB(details);
|
||||
}
|
||||
@Override
|
||||
public PhoneEntity getDetails () {
|
||||
return phoneVerificationService.getPhoneEntityByQRCodeId(data.getId());
|
||||
}
|
||||
}
|
||||
13
src/main/java/com/safeqr/app/qrcode/model/QRCodeModel.java
Normal file
13
src/main/java/com/safeqr/app/qrcode/model/QRCodeModel.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.safeqr.app.qrcode.model;
|
||||
|
||||
import com.safeqr.app.qrcode.entity.QRCodeEntity;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public abstract class QRCodeModel<T>{
|
||||
QRCodeEntity data;
|
||||
T details;
|
||||
|
||||
public abstract void setDetails();
|
||||
public abstract T getDetails();
|
||||
}
|
||||
37
src/main/java/com/safeqr/app/qrcode/model/SMSModel.java
Normal file
37
src/main/java/com/safeqr/app/qrcode/model/SMSModel.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package com.safeqr.app.qrcode.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.safeqr.app.qrcode.entity.EmailEntity;
|
||||
import com.safeqr.app.qrcode.entity.QRCodeEntity;
|
||||
import com.safeqr.app.qrcode.entity.SMSEntity;
|
||||
import com.safeqr.app.qrcode.service.SMSVerificationService;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public final class SMSModel extends QRCodeModel<SMSEntity> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(SMSModel.class);
|
||||
|
||||
@JsonIgnore
|
||||
private final SMSVerificationService smsVerificationService;
|
||||
|
||||
public SMSModel(QRCodeEntity scannedQRCodeEntity, SMSVerificationService smsVerificationService) {
|
||||
this.data = scannedQRCodeEntity;
|
||||
this.smsVerificationService = smsVerificationService;
|
||||
this.details = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDetails() {
|
||||
details = SMSEntity.builder().qrCodeId(data.getId()).build();
|
||||
// Insert into sms table
|
||||
smsVerificationService.insertDB(details);
|
||||
}
|
||||
@Override
|
||||
public SMSEntity getDetails () {
|
||||
return smsVerificationService.getSMSEntityByQRCodeId(data.getId());
|
||||
}
|
||||
}
|
||||
37
src/main/java/com/safeqr/app/qrcode/model/TextModel.java
Normal file
37
src/main/java/com/safeqr/app/qrcode/model/TextModel.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package com.safeqr.app.qrcode.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.safeqr.app.qrcode.entity.EmailEntity;
|
||||
import com.safeqr.app.qrcode.entity.QRCodeEntity;
|
||||
import com.safeqr.app.qrcode.entity.TextEntity;
|
||||
import com.safeqr.app.qrcode.service.TextVerificationService;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public final class TextModel extends QRCodeModel<TextEntity> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(TextModel.class);
|
||||
|
||||
@JsonIgnore
|
||||
private final TextVerificationService textVerificationService;
|
||||
|
||||
public TextModel(QRCodeEntity scannedQRCodeEntity, TextVerificationService textVerificationService) {
|
||||
this.data = scannedQRCodeEntity;
|
||||
this.textVerificationService = textVerificationService;
|
||||
this.details = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDetails() {
|
||||
details = TextEntity.builder().qrCodeId(data.getId()).text(data.getContents()).build();
|
||||
// Insert into text table
|
||||
textVerificationService.insertDB(details);
|
||||
}
|
||||
@Override
|
||||
public TextEntity getDetails () {
|
||||
return textVerificationService.getTextEntityByQRCodeId(data.getId());
|
||||
}
|
||||
}
|
||||
50
src/main/java/com/safeqr/app/qrcode/model/URLModel.java
Normal file
50
src/main/java/com/safeqr/app/qrcode/model/URLModel.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package com.safeqr.app.qrcode.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.safeqr.app.qrcode.entity.EmailEntity;
|
||||
import com.safeqr.app.qrcode.entity.QRCodeEntity;
|
||||
import com.safeqr.app.qrcode.entity.URLEntity;
|
||||
import com.safeqr.app.qrcode.service.URLVerificationService;
|
||||
import jakarta.transaction.Transactional;
|
||||
import lombok.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public final class URLModel extends QRCodeModel<URLEntity> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(URLModel.class);
|
||||
@JsonIgnore
|
||||
private final URLVerificationService urlVerificationService;
|
||||
|
||||
@Autowired
|
||||
public URLModel(QRCodeEntity scannedQRCodeEntity, URLVerificationService urlVerificationService) {
|
||||
this.data = scannedQRCodeEntity;
|
||||
this.urlVerificationService = urlVerificationService;
|
||||
details = null;
|
||||
}
|
||||
@Transactional
|
||||
@Override
|
||||
public void setDetails() {
|
||||
String url = data.getContents();
|
||||
try {
|
||||
details = urlVerificationService.breakdownURL(url);
|
||||
urlVerificationService.countAndTrackRedirects(url, details);
|
||||
// set qrCode Identifier
|
||||
details.setQrCodeId(data.getId());
|
||||
// Insert into URL table
|
||||
urlVerificationService.insertDB(details);
|
||||
|
||||
} catch (IOException e) {
|
||||
logger.error("Error: ", e);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public URLEntity getDetails () {
|
||||
return urlVerificationService.getURLEntityByQRCodeId(data.getId());
|
||||
}
|
||||
}
|
||||
36
src/main/java/com/safeqr/app/qrcode/model/WifiModel.java
Normal file
36
src/main/java/com/safeqr/app/qrcode/model/WifiModel.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package com.safeqr.app.qrcode.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.safeqr.app.qrcode.entity.QRCodeEntity;
|
||||
import com.safeqr.app.qrcode.entity.WifiEntity;
|
||||
import com.safeqr.app.qrcode.service.WifiVerificationService;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public final class WifiModel extends QRCodeModel<WifiEntity> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(WifiModel.class);
|
||||
|
||||
@JsonIgnore
|
||||
private final WifiVerificationService wifiVerificationService;
|
||||
|
||||
public WifiModel(QRCodeEntity scannedQRCodeEntity, WifiVerificationService wifiVerificationService) {
|
||||
this.data = scannedQRCodeEntity;
|
||||
this.wifiVerificationService = wifiVerificationService;
|
||||
this.details = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDetails() {
|
||||
details = WifiEntity.builder().qrCodeId(data.getId()).build();
|
||||
// Insert into wifi table
|
||||
wifiVerificationService.insertDB(details);
|
||||
}
|
||||
@Override
|
||||
public WifiEntity getDetails () {
|
||||
return wifiVerificationService.getWifiEntityByQRCodeId(data.getId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.safeqr.app.qrcode.model.factory;
|
||||
|
||||
import com.safeqr.app.qrcode.entity.QRCodeEntity;
|
||||
import com.safeqr.app.qrcode.model.EmailModel;
|
||||
import com.safeqr.app.qrcode.service.EmailVerificationService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class EmailFactory implements QRCodeFactory<EmailModel> {
|
||||
private final EmailVerificationService emailVerificationService;
|
||||
|
||||
@Autowired
|
||||
public EmailFactory(EmailVerificationService emailVerificationService) {
|
||||
this.emailVerificationService = emailVerificationService;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public EmailModel create(QRCodeEntity scannedQRCodeEntity) {
|
||||
return new EmailModel(scannedQRCodeEntity, emailVerificationService);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.safeqr.app.qrcode.model.factory;
|
||||
|
||||
import com.safeqr.app.qrcode.entity.QRCodeEntity;
|
||||
import com.safeqr.app.qrcode.model.PhoneModel;
|
||||
import com.safeqr.app.qrcode.service.PhoneVerificationService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class PhoneFactory implements QRCodeFactory<PhoneModel> {
|
||||
private final PhoneVerificationService phoneVerificationService;
|
||||
|
||||
@Autowired
|
||||
public PhoneFactory(PhoneVerificationService phoneVerificationService) {
|
||||
this.phoneVerificationService = phoneVerificationService;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public PhoneModel create(QRCodeEntity scannedQRCodeEntity) {
|
||||
return new PhoneModel(scannedQRCodeEntity, phoneVerificationService);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.safeqr.app.qrcode.model.factory;
|
||||
|
||||
import com.safeqr.app.qrcode.entity.QRCodeEntity;
|
||||
import com.safeqr.app.qrcode.model.QRCodeModel;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface QRCodeFactory<T extends QRCodeModel<?>> {
|
||||
T create(QRCodeEntity scannedQRCodeEntity);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.safeqr.app.qrcode.model.factory;
|
||||
|
||||
import com.safeqr.app.qrcode.entity.QRCodeEntity;
|
||||
import com.safeqr.app.qrcode.model.QRCodeModel;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import static com.safeqr.app.constants.CommonConstants.*;
|
||||
|
||||
|
||||
@Component
|
||||
public class QRCodeFactoryProvider {
|
||||
private final ApplicationContext applicationContext;
|
||||
@Autowired
|
||||
public QRCodeFactoryProvider(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
public QRCodeModel createQRCodeInstance(QRCodeEntity scannedQRCodeEntity) {
|
||||
return switch (scannedQRCodeEntity.getInfo().getType().toUpperCase()) {
|
||||
case QR_CODE_TYPE_URL-> applicationContext.getBean(URLFactory.class).create(scannedQRCodeEntity);
|
||||
case QR_CODE_TYPE_PHONE -> applicationContext.getBean(PhoneFactory.class).create(scannedQRCodeEntity);
|
||||
case QR_CODE_TYPE_SMS -> applicationContext.getBean(SMSFactory.class).create(scannedQRCodeEntity);
|
||||
case QR_CODE_TYPE_EMAIL -> applicationContext.getBean(EmailFactory.class).create(scannedQRCodeEntity);
|
||||
case QR_CODE_TYPE_WIFI -> applicationContext.getBean(WifiFactory.class).create(scannedQRCodeEntity);
|
||||
case DEFAULT_QR_CODE_TYPE -> applicationContext.getBean(TextFactory.class).create(scannedQRCodeEntity);
|
||||
default -> throw new IllegalArgumentException("Unsupported QR code type: " + scannedQRCodeEntity.getInfo().getType());
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.safeqr.app.qrcode.model.factory;
|
||||
|
||||
import com.safeqr.app.qrcode.entity.QRCodeEntity;
|
||||
import com.safeqr.app.qrcode.model.SMSModel;
|
||||
import com.safeqr.app.qrcode.service.SMSVerificationService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class SMSFactory implements QRCodeFactory<SMSModel> {
|
||||
private final SMSVerificationService smsVerificationService;
|
||||
|
||||
@Autowired
|
||||
public SMSFactory(SMSVerificationService smsVerificationService) {
|
||||
this.smsVerificationService = smsVerificationService;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public SMSModel create(QRCodeEntity scannedQRCodeEntity) {
|
||||
return new SMSModel(scannedQRCodeEntity, smsVerificationService);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.safeqr.app.qrcode.model.factory;
|
||||
|
||||
import com.safeqr.app.qrcode.entity.QRCodeEntity;
|
||||
import com.safeqr.app.qrcode.model.TextModel;
|
||||
import com.safeqr.app.qrcode.service.TextVerificationService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class TextFactory implements QRCodeFactory<TextModel> {
|
||||
private final TextVerificationService textVerificationService;
|
||||
|
||||
@Autowired
|
||||
public TextFactory(TextVerificationService textVerificationService) {
|
||||
this.textVerificationService = textVerificationService;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public TextModel create(QRCodeEntity scannedQRCodeEntity) {
|
||||
return new TextModel(scannedQRCodeEntity, textVerificationService);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.safeqr.app.qrcode.model.factory;
|
||||
|
||||
import com.safeqr.app.qrcode.entity.QRCodeEntity;
|
||||
import com.safeqr.app.qrcode.model.URLModel;
|
||||
import com.safeqr.app.qrcode.service.URLVerificationService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class URLFactory implements QRCodeFactory<URLModel> {
|
||||
private final URLVerificationService urlVerificationService;
|
||||
|
||||
@Autowired
|
||||
public URLFactory(URLVerificationService urlVerificationService) {
|
||||
this.urlVerificationService = urlVerificationService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URLModel create(QRCodeEntity scannedQRCodeEntity) {
|
||||
return new URLModel(scannedQRCodeEntity, urlVerificationService);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.safeqr.app.qrcode.model.factory;
|
||||
|
||||
import com.safeqr.app.qrcode.entity.QRCodeEntity;
|
||||
import com.safeqr.app.qrcode.model.WifiModel;
|
||||
import com.safeqr.app.qrcode.service.WifiVerificationService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class WifiFactory implements QRCodeFactory<WifiModel> {
|
||||
private final WifiVerificationService wifiVerificationService;
|
||||
|
||||
@Autowired
|
||||
public WifiFactory(WifiVerificationService wifiVerificationService) {
|
||||
this.wifiVerificationService = wifiVerificationService;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public WifiModel create(QRCodeEntity scannedQRCodeEntity) {
|
||||
return new WifiModel(scannedQRCodeEntity, wifiVerificationService);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.safeqr.app.qrcode.repository;
|
||||
|
||||
import com.safeqr.app.qrcode.entity.EmailEntity;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface EmailRepository extends GenericRepository<EmailEntity> {
|
||||
Optional<EmailEntity> findByQrCodeId(UUID qrCodeId);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.safeqr.app.qrcode.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.repository.NoRepositoryBean;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@NoRepositoryBean
|
||||
public interface GenericRepository<T> extends JpaRepository<T, UUID> {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.safeqr.app.qrcode.repository;
|
||||
|
||||
import com.safeqr.app.qrcode.entity.PhoneEntity;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface PhoneRepository extends GenericRepository<PhoneEntity> {
|
||||
Optional<PhoneEntity> findByQrCodeId(UUID qrCodeId);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.safeqr.app.qrcode.repository;
|
||||
|
||||
import com.safeqr.app.qrcode.entity.QRCodeEntity;
|
||||
|
||||
public interface QRCodeRepository extends GenericRepository<QRCodeEntity> {
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
|
||||
package com.safeqr.app.qrcode.repository;
|
||||
|
||||
import com.safeqr.app.qrcode.entity.QRCodeType;
|
||||
import com.safeqr.app.qrcode.entity.QRCodeTypeEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface QRCodeTypeRepository extends JpaRepository<QRCodeType, Long> {
|
||||
public interface QRCodeTypeRepository extends JpaRepository<QRCodeTypeEntity, Long> {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.safeqr.app.qrcode.repository;
|
||||
|
||||
import com.safeqr.app.qrcode.entity.SMSEntity;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface SMSRepository extends GenericRepository<SMSEntity> {
|
||||
Optional<SMSEntity> findByQrCodeId(UUID qrCodeId);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.safeqr.app.qrcode.repository;
|
||||
|
||||
import com.safeqr.app.qrcode.entity.QRCodeEntity;
|
||||
import com.safeqr.app.qrcode.entity.ScanBookmarkEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface ScanBookmarkRepository extends JpaRepository<ScanBookmarkEntity, Long> {
|
||||
@Query("SELECT sb.qrCodeEntity FROM ScanBookmarkEntity sb WHERE sb.userId = :userId AND sb.bookmarkStatus = 'ACTIVE'")
|
||||
List<QRCodeEntity> findAllBookmarksByUserId(String userId);
|
||||
|
||||
@Query("SELECT sb FROM ScanBookmarkEntity sb WHERE sb.userId = :userId AND sb.qrCodeId = :qrCodeId AND sb.bookmarkStatus = 'ACTIVE'")
|
||||
Optional<ScanBookmarkEntity> findByUserIdAndQrCodeId(String userId, UUID qrCodeId);
|
||||
|
||||
@Modifying
|
||||
@Query("UPDATE ScanBookmarkEntity sb SET sb.bookmarkStatus = com.safeqr.app.qrcode.entity.ScanBookmarkEntity$BookmarkStatus.INACTIVE, sb.dateUpdated = CURRENT_TIMESTAMP WHERE sb.userId = :userId AND sb.bookmarkStatus = com.safeqr.app.qrcode.entity.ScanBookmarkEntity$BookmarkStatus.ACTIVE AND sb.qrCodeId = :qrCodeId")
|
||||
int updateBookmarkStatusToInactive(String userId, UUID qrCodeId);
|
||||
|
||||
@Modifying
|
||||
@Query("UPDATE ScanBookmarkEntity sb SET sb.bookmarkStatus = com.safeqr.app.qrcode.entity.ScanBookmarkEntity$BookmarkStatus.INACTIVE, sb.dateUpdated = CURRENT_TIMESTAMP WHERE sb.userId = :userId AND sb.bookmarkStatus = com.safeqr.app.qrcode.entity.ScanBookmarkEntity$BookmarkStatus.ACTIVE")
|
||||
int updateBookmarkStatusToInactiveByUserId(String userId);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.safeqr.app.qrcode.repository;
|
||||
|
||||
import com.safeqr.app.qrcode.entity.QRCodeEntity;
|
||||
import com.safeqr.app.qrcode.entity.ScanBookmarkEntity;
|
||||
import com.safeqr.app.qrcode.entity.ScanHistoryEntity;
|
||||
import com.safeqr.app.user.dto.ScannedHistoriesDto;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface ScanHistoryRepository extends JpaRepository<ScanHistoryEntity, Long> {
|
||||
@Query("SELECT new com.safeqr.app.user.dto.ScannedHistoriesDto(sh.qrCodeEntity, sh.bookmarked) FROM ScanHistoryEntity sh WHERE sh.userId = :userId AND sh.scanStatus = 'ACTIVE' ORDER BY sh.dateCreated DESC")
|
||||
List<ScannedHistoriesDto> findAllQRCodesByUserId(@Param("userId") String userId);
|
||||
|
||||
@Modifying
|
||||
@Query("UPDATE ScanHistoryEntity sh SET sh.scanStatus = com.safeqr.app.qrcode.entity.ScanHistoryEntity$ScanStatus.INACTIVE, sh.dateUpdated = CURRENT_TIMESTAMP WHERE sh.userId = :userId AND sh.scanStatus = com.safeqr.app.qrcode.entity.ScanHistoryEntity$ScanStatus.ACTIVE AND sh.qrCodeId = :qrCodeId")
|
||||
int updateScannedHistoryToInactive(String userId, UUID qrCodeId);
|
||||
|
||||
@Modifying
|
||||
@Query("UPDATE ScanHistoryEntity sh SET sh.scanStatus = com.safeqr.app.qrcode.entity.ScanHistoryEntity$ScanStatus.INACTIVE, sh.dateUpdated = CURRENT_TIMESTAMP WHERE sh.userId = :userId AND sh.scanStatus = com.safeqr.app.qrcode.entity.ScanHistoryEntity$ScanStatus.ACTIVE")
|
||||
int updateScannedHistoriesToInactiveByUserId(String userId);
|
||||
@Query("SELECT sh FROM ScanHistoryEntity sh WHERE sh.userId = :userId AND sh.qrCodeId = :qrCodeId AND sh.bookmarked = true AND sh.scanStatus = 'ACTIVE'")
|
||||
Optional<ScanHistoryEntity> findByUserIdAndQrCodeId(String userId, UUID qrCodeId);
|
||||
|
||||
@Query("SELECT new com.safeqr.app.user.dto.ScannedHistoriesDto(sh.qrCodeEntity, sh.bookmarked) FROM ScanHistoryEntity sh WHERE sh.userId = :userId AND sh.bookmarked = true AND sh.scanStatus = 'ACTIVE' ORDER BY sh.dateCreated DESC")
|
||||
List<ScannedHistoriesDto> findAllBookmarksByUserId(@Param("userId") String userId);
|
||||
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Query("UPDATE ScanHistoryEntity sh SET sh.bookmarked = true, sh.dateUpdated = CURRENT_TIMESTAMP " +
|
||||
"WHERE sh.userId = :userId AND sh.bookmarked = false AND sh.qrCodeId = :qrCodeId AND sh.scanStatus = 'ACTIVE'")
|
||||
int updateBookmarkStatusToActive(String userId, UUID qrCodeId);
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Query("UPDATE ScanHistoryEntity sh SET sh.bookmarked = false, sh.dateUpdated = CURRENT_TIMESTAMP " +
|
||||
"WHERE sh.userId = :userId AND sh.bookmarked = true AND sh.qrCodeId = :qrCodeId AND sh.scanStatus = 'ACTIVE'")
|
||||
int updateBookmarkStatusToInactive(String userId, UUID qrCodeId);
|
||||
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Query("UPDATE ScanHistoryEntity sh SET sh.bookmarked = false, sh.dateUpdated = CURRENT_TIMESTAMP WHERE sh.userId = :userId AND sh.bookmarked = true AND sh.scanStatus = 'ACTIVE'")
|
||||
int updateBookmarkStatusToInactiveByUserId(String userId);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.safeqr.app.qrcode.repository;
|
||||
|
||||
import com.safeqr.app.qrcode.entity.TextEntity;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface TextRepository extends GenericRepository<TextEntity> {
|
||||
Optional<TextEntity> findByQrCodeId(UUID qrCodeId);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.safeqr.app.qrcode.repository;
|
||||
|
||||
import com.safeqr.app.qrcode.entity.URLEntity;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface URLRepository extends GenericRepository<URLEntity> {
|
||||
Optional<URLEntity> findByQrCodeId(UUID qrCodeId);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.safeqr.app.qrcode.repository;
|
||||
|
||||
import com.safeqr.app.qrcode.entity.WifiEntity;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface WifiRepository extends GenericRepository<WifiEntity> {
|
||||
Optional<WifiEntity> findByQrCodeId(UUID qrCodeId);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.safeqr.app.qrcode.service;
|
||||
|
||||
import com.safeqr.app.exceptions.ResourceNotFoundExceptions;
|
||||
import com.safeqr.app.qrcode.entity.EmailEntity;
|
||||
import com.safeqr.app.qrcode.repository.EmailRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
public class EmailVerificationService {
|
||||
private final EmailRepository emailRepository;
|
||||
private static final Logger logger = LoggerFactory.getLogger(EmailVerificationService.class);
|
||||
|
||||
@Autowired
|
||||
public EmailVerificationService(EmailRepository emailRepository) {
|
||||
this.emailRepository = emailRepository;
|
||||
}
|
||||
public EmailEntity getEmailEntityByQRCodeId(UUID qrCodeId) {
|
||||
logger.info("qrCodeId retrieving: {}", qrCodeId);
|
||||
return emailRepository.findByQrCodeId(qrCodeId)
|
||||
.orElseThrow(() -> new ResourceNotFoundExceptions("Email not found for QR Code id: " + qrCodeId));
|
||||
}
|
||||
public void insertDB(EmailEntity emailEntity) {
|
||||
emailRepository.save(emailEntity);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.safeqr.app.qrcode.service;
|
||||
|
||||
import com.safeqr.app.exceptions.ResourceNotFoundExceptions;
|
||||
import com.safeqr.app.qrcode.entity.PhoneEntity;
|
||||
import com.safeqr.app.qrcode.repository.PhoneRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
public class PhoneVerificationService {
|
||||
private final PhoneRepository phoneRepository;
|
||||
private static final Logger logger = LoggerFactory.getLogger(PhoneVerificationService.class);
|
||||
|
||||
@Autowired
|
||||
public PhoneVerificationService(PhoneRepository phoneRepository) {
|
||||
this.phoneRepository = phoneRepository;
|
||||
}
|
||||
public PhoneEntity getPhoneEntityByQRCodeId(UUID qrCodeId) {
|
||||
logger.info("qrCodeId retrieving: {}", qrCodeId);
|
||||
return phoneRepository.findByQrCodeId(qrCodeId)
|
||||
.orElseThrow(() -> new ResourceNotFoundExceptions("Phone not found for QR Code id: " + qrCodeId));
|
||||
}
|
||||
public void insertDB(PhoneEntity phoneEntity) {
|
||||
phoneRepository.save(phoneEntity);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,34 +1,126 @@
|
||||
|
||||
package com.safeqr.app.qrcode.service;
|
||||
|
||||
import com.safeqr.app.qrcode.dto.QRCodePayload;
|
||||
import com.safeqr.app.qrcode.entity.QRCodeType;
|
||||
import static com.safeqr.app.constants.CommonConstants.*;
|
||||
|
||||
import com.safeqr.app.exceptions.ResourceNotFoundExceptions;
|
||||
import com.safeqr.app.qrcode.dto.request.QRCodePayload;
|
||||
import com.safeqr.app.qrcode.dto.response.BaseScanResponse;
|
||||
import com.safeqr.app.qrcode.entity.QRCodeEntity;
|
||||
import com.safeqr.app.qrcode.entity.QRCodeTypeEntity;
|
||||
import com.safeqr.app.qrcode.entity.ScanHistoryEntity;
|
||||
import com.safeqr.app.qrcode.model.factory.QRCodeFactoryProvider;
|
||||
import com.safeqr.app.qrcode.model.QRCodeModel;
|
||||
import com.safeqr.app.qrcode.repository.QRCodeRepository;
|
||||
import com.safeqr.app.qrcode.repository.QRCodeTypeRepository;
|
||||
import com.safeqr.app.qrcode.repository.ScanHistoryRepository;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
public class QRCodeTypeService {
|
||||
private static final Logger logger = LoggerFactory.getLogger(QRCodeTypeService.class);
|
||||
|
||||
private final QRCodeFactoryProvider qrCodeFactoryProvider;
|
||||
private final QRCodeTypeRepository qrCodeTypeRepository;
|
||||
private final ScanHistoryRepository scanHistoryRepository;
|
||||
private final QRCodeRepository qrCodeRepository;
|
||||
private final SafeBrowsingService safeBrowsingService;
|
||||
@Autowired
|
||||
private QRCodeTypeRepository qrCodeTypeRepository;
|
||||
public QRCodeTypeService(QRCodeFactoryProvider qrCodeFactoryProvider,
|
||||
QRCodeTypeRepository qrCodeTypeRepository,
|
||||
ScanHistoryRepository scanHistoryRepository,
|
||||
QRCodeRepository qrCodeRepository,
|
||||
SafeBrowsingService safeBrowsingService
|
||||
) {
|
||||
this.qrCodeFactoryProvider = qrCodeFactoryProvider;
|
||||
this.qrCodeTypeRepository = qrCodeTypeRepository;
|
||||
this.scanHistoryRepository = scanHistoryRepository;
|
||||
this.qrCodeRepository = qrCodeRepository;
|
||||
this.safeBrowsingService = safeBrowsingService;
|
||||
}
|
||||
private List<QRCodeTypeEntity> configs;
|
||||
private QRCodeTypeEntity defaultQRCodeTypeEntity;
|
||||
|
||||
@Autowired
|
||||
private SafeBrowsingService safeBrowsingService;
|
||||
@PostConstruct
|
||||
public void loadQRCodeTypes() {
|
||||
// Fetch all QR Code Types from the database
|
||||
configs = qrCodeTypeRepository.findAll();
|
||||
// Set the default QR Code Type
|
||||
defaultQRCodeTypeEntity = configs.stream()
|
||||
.filter(config -> config.getType().equals(DEFAULT_QR_CODE_TYPE))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public List<QRCodeType> getAllTypes() {
|
||||
return qrCodeTypeRepository.findAll();
|
||||
public List<QRCodeTypeEntity> getAllTypes() {
|
||||
return configs;
|
||||
}
|
||||
// Get scanned qrcode details
|
||||
public BaseScanResponse getScannedQRCodeDetails(UUID qrCodeId){
|
||||
// Find scanned qr code in qr code table
|
||||
QRCodeEntity qrCodeEntity = qrCodeRepository.findById(qrCodeId)
|
||||
.orElseThrow(() -> new ResourceNotFoundExceptions("QR Code not found with id: " + qrCodeId));
|
||||
logger.info("qrCodeEntity: {}", qrCodeEntity);
|
||||
QRCodeModel<?> qrCodeModel = qrCodeFactoryProvider.createQRCodeInstance(qrCodeEntity);
|
||||
logger.info("Retrieved details: {}", qrCodeModel.getDetails());
|
||||
return BaseScanResponse.builder().qrcode(qrCodeModel).build();
|
||||
}
|
||||
|
||||
// Process Scanned QR Code
|
||||
@Transactional
|
||||
public BaseScanResponse scanQRCode(String userId, QRCodePayload payload) {
|
||||
String data = payload.getData();
|
||||
logger.info("scanQRCode: userId={}, data={}", userId, data);
|
||||
|
||||
// Get the QR Code Type
|
||||
QRCodeTypeEntity qrType = getQRCodeType(data);
|
||||
|
||||
// Insert the QR Code into main qrcode table
|
||||
QRCodeEntity scannedQR = qrCodeRepository.save(QRCodeEntity.builder()
|
||||
.userId(userId)
|
||||
.contents(data)
|
||||
.info(qrType)
|
||||
.createdAt(LocalDateTime.now())
|
||||
.build());
|
||||
|
||||
// Insert into Scan History table if userId is not null
|
||||
logger.info("scanQRCode: scannedQR new ID={}", scannedQR.getId());
|
||||
if (userId != null) {
|
||||
scanHistoryRepository.save(ScanHistoryEntity.builder()
|
||||
.qrCodeId(scannedQR.getId())
|
||||
.userId(userId)
|
||||
.scanStatus(ScanHistoryEntity.ScanStatus.ACTIVE)
|
||||
.build());
|
||||
}
|
||||
// Create the QR Code Instance based on the QR Code Type & insert into the respective table
|
||||
QRCodeModel<?> qrCodeModel = qrCodeFactoryProvider.createQRCodeInstance(scannedQR);
|
||||
qrCodeModel.setDetails();
|
||||
|
||||
return BaseScanResponse.builder().qrcode(qrCodeModel).build();
|
||||
}
|
||||
// Returns Default type as text if it does not fit into any of the category
|
||||
private QRCodeTypeEntity getQRCodeType(String data) {
|
||||
return configs.stream()
|
||||
.filter(config -> data.toLowerCase().startsWith(config.getPrefix().toLowerCase()))
|
||||
.findFirst()
|
||||
.orElse(defaultQRCodeTypeEntity);
|
||||
}
|
||||
|
||||
public Mono<String> detectType(QRCodePayload payload) {
|
||||
String data = payload.getData();
|
||||
List<QRCodeType> configs = qrCodeTypeRepository.findAll();
|
||||
|
||||
for (QRCodeType config : configs) {
|
||||
for (QRCodeTypeEntity config : configs) {
|
||||
if (data.startsWith(config.getPrefix())) {
|
||||
if ("URL".equals(config.getType())) {
|
||||
try
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
|
||||
package com.safeqr.app.qrcode.service;
|
||||
|
||||
import com.safeqr.app.qrcode.dto.QRCodePayload;
|
||||
import com.safeqr.app.qrcode.dto.request.QRCodePayload;
|
||||
import com.safeqr.app.qrcode.dto.RedirectCountResponse;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import reactor.core.publisher.Mono;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@Service
|
||||
public class RedirectCountService {
|
||||
private static final Logger logger = LoggerFactory.getLogger(RedirectCountService.class);
|
||||
|
||||
public Mono<RedirectCountResponse> countRedirects(QRCodePayload payload) {
|
||||
String url = payload.getData();
|
||||
logger.info("RedirectCountService: countRedirects: url={}", url);
|
||||
|
||||
return WebClient.create()
|
||||
.get()
|
||||
.uri(url)
|
||||
.uri("https://google.com")// replace with url when logic is complete
|
||||
.exchangeToMono(response -> {
|
||||
RedirectCountResponse redirectCountResponse = new RedirectCountResponse();
|
||||
redirectCountResponse.setRedirectCount(response.cookies().size());
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.safeqr.app.qrcode.service;
|
||||
|
||||
import com.safeqr.app.exceptions.ResourceNotFoundExceptions;
|
||||
import com.safeqr.app.qrcode.entity.SMSEntity;
|
||||
import com.safeqr.app.qrcode.repository.SMSRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
public class SMSVerificationService {
|
||||
private final SMSRepository smsRepository;
|
||||
private static final Logger logger = LoggerFactory.getLogger(SMSVerificationService.class);
|
||||
|
||||
@Autowired
|
||||
public SMSVerificationService(SMSRepository smsRepository) {
|
||||
this.smsRepository = smsRepository;
|
||||
}
|
||||
|
||||
public SMSEntity getSMSEntityByQRCodeId(UUID qrCodeId) {
|
||||
logger.info("qrCodeId retrieving: {}", qrCodeId);
|
||||
return smsRepository.findByQrCodeId(qrCodeId)
|
||||
.orElseThrow(() -> new ResourceNotFoundExceptions("SMS not found for QR Code id: " + qrCodeId));
|
||||
}
|
||||
public void insertDB(SMSEntity smsEntity) {
|
||||
smsRepository.save(smsEntity);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.safeqr.app.qrcode.service;
|
||||
|
||||
import com.safeqr.app.exceptions.ResourceNotFoundExceptions;
|
||||
import com.safeqr.app.qrcode.entity.TextEntity;
|
||||
import com.safeqr.app.qrcode.repository.TextRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
public class TextVerificationService {
|
||||
private final TextRepository textRepository;
|
||||
private static final Logger logger = LoggerFactory.getLogger(TextVerificationService.class);
|
||||
@Autowired
|
||||
public TextVerificationService(TextRepository textRepository) {
|
||||
this.textRepository = textRepository;
|
||||
}
|
||||
|
||||
public TextEntity getTextEntityByQRCodeId(UUID qrCodeId) {
|
||||
logger.info("qrCodeId retrieving: {}", qrCodeId);
|
||||
return textRepository.findByQrCodeId(qrCodeId)
|
||||
.orElseThrow(() -> new ResourceNotFoundExceptions("Text not found for QR Code id: " + qrCodeId));
|
||||
}
|
||||
public void insertDB(TextEntity textEntity) {
|
||||
textRepository.save(textEntity);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,207 @@
|
||||
package com.safeqr.app.qrcode.service;
|
||||
|
||||
import com.safeqr.app.qrcode.dto.QRCodePayload;
|
||||
import static com.safeqr.app.constants.CommonConstants.*;
|
||||
|
||||
import com.safeqr.app.exceptions.ResourceNotFoundExceptions;
|
||||
import com.safeqr.app.qrcode.dto.request.QRCodePayload;
|
||||
import com.safeqr.app.qrcode.dto.URLVerificationResponse;
|
||||
import com.safeqr.app.qrcode.entity.URLEntity;
|
||||
import com.safeqr.app.qrcode.repository.URLRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
@Service
|
||||
public class URLVerificationService {
|
||||
private static final Logger logger = LoggerFactory.getLogger(URLVerificationService.class);
|
||||
private final URLRepository urlRepository;
|
||||
@Autowired
|
||||
public URLVerificationService(URLRepository urlRepository) {
|
||||
this.urlRepository = urlRepository;
|
||||
}
|
||||
|
||||
public URLEntity getURLEntityByQRCodeId(UUID qrCodeId) {
|
||||
logger.info("qrCodeId retrieving: {}", qrCodeId);
|
||||
return urlRepository.findByQrCodeId(qrCodeId)
|
||||
.orElseThrow(() -> new ResourceNotFoundExceptions("URL not found for QR Code id: " + qrCodeId));
|
||||
}
|
||||
|
||||
public void insertDB(URLEntity urlEntity) {
|
||||
urlRepository.save(urlEntity);
|
||||
}
|
||||
// Function to breakdown URL into subdomain, domain, topLevelDomain, query params, fragment
|
||||
public URLEntity breakdownURL(String urlString) throws MalformedURLException {
|
||||
URLEntity urlObj = new URLEntity();
|
||||
try {
|
||||
// Ensure the URL is properly encoded
|
||||
String encodedUrl = encodeUrl(urlString);
|
||||
URI uri = new URI(encodedUrl);
|
||||
URL url = uri.toURL();
|
||||
|
||||
String host = url.getHost();
|
||||
// split host into subdomain, domain, topLevelDomain
|
||||
String[] hostParts = host.split("\\.");
|
||||
String subdomain = "";
|
||||
|
||||
if (hostParts.length >= 2) {
|
||||
// set topLevelDomain to the last part of the host
|
||||
urlObj.setTopLevelDomain(hostParts[hostParts.length - 1]);
|
||||
// set domain to the second last part of the host
|
||||
urlObj.setDomain(hostParts[hostParts.length - 2]);
|
||||
// set subdomain to the first part of the host
|
||||
if (hostParts.length > 2) {
|
||||
subdomain = String.join(".", java.util.Arrays.copyOfRange(hostParts, 0, hostParts.length - 2));
|
||||
}
|
||||
}
|
||||
// set subdomain to URL host
|
||||
urlObj.setSubdomain(subdomain);
|
||||
|
||||
String path = url.getPath();
|
||||
//set path to URL path if it's not empty, otherwise set it to root path
|
||||
urlObj.setPath(path.isEmpty() ? "/" : path);
|
||||
|
||||
String query = url.getQuery();
|
||||
Map<String, String> queryParams = new HashMap<>();
|
||||
if (query != null) {
|
||||
// split query params into key value pairs
|
||||
for (String param : query.split("&")) {
|
||||
String[] pair = param.split("=");
|
||||
queryParams.put(pair[0], pair.length > 1 ? pair[1] : "");
|
||||
}
|
||||
logger.info("queryParams: {}", queryParams);
|
||||
}
|
||||
// set query params to URL query
|
||||
urlObj.setQuery(queryParams.toString());
|
||||
// set fragment to URL ref
|
||||
urlObj.setFragment(Optional.ofNullable(url.getRef()).orElse(""));
|
||||
} catch (URISyntaxException | MalformedURLException e) {
|
||||
logger.error("Error in breaking down URL: {}", e.getMessage());
|
||||
}
|
||||
return urlObj;
|
||||
}
|
||||
private String encodeUrl(String urlString) throws MalformedURLException {
|
||||
try {
|
||||
URL url = new URL(urlString);
|
||||
String protocol = url.getProtocol();
|
||||
String host = url.getHost();
|
||||
int port = url.getPort();
|
||||
String path = url.getPath();
|
||||
String query = url.getQuery();
|
||||
String ref = url.getRef();
|
||||
|
||||
StringBuilder encodedUrl = new StringBuilder();
|
||||
encodedUrl.append(protocol).append("://").append(host);
|
||||
if (port != -1) {
|
||||
encodedUrl.append(":").append(port);
|
||||
}
|
||||
encodedUrl.append(URLEncoder.encode(path, StandardCharsets.UTF_8).replace("%2F", "/"));
|
||||
|
||||
if (query != null) {
|
||||
encodedUrl.append("?").append(URLEncoder.encode(query, StandardCharsets.UTF_8).replace("%3D", "=").replace("%26", "&"));
|
||||
}
|
||||
if (ref != null) {
|
||||
encodedUrl.append("#").append(URLEncoder.encode(ref, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
return encodedUrl.toString();
|
||||
} catch (Exception e) {
|
||||
throw new MalformedURLException("Failed to encode URL: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void countAndTrackRedirects(String urlString, URLEntity details) throws IOException {
|
||||
try {
|
||||
URI uri = new URI(urlString);
|
||||
URL url = uri.toURL();
|
||||
List<String> redirectChain = new ArrayList<>();
|
||||
List<String> hstsHeaderList = new ArrayList<>();
|
||||
List<Boolean> sslStrippingList = new ArrayList<>();
|
||||
|
||||
// Add the initial URL to the chain
|
||||
redirectChain.add(urlString);
|
||||
boolean redirected;
|
||||
int redirectCount = 0;
|
||||
|
||||
do {
|
||||
URLConnection testConnection = url.openConnection();
|
||||
|
||||
if (!(testConnection instanceof HttpURLConnection)) {
|
||||
// Handle non-HTTP connections (like mailto:)
|
||||
logger.info("Non-HTTP URL encountered: {}", url);
|
||||
hstsHeaderList.add(INFO_HSTS_NOT_APPLICABLE);
|
||||
sslStrippingList.add(false);
|
||||
break;
|
||||
}
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setInstanceFollowRedirects(false);
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
redirected = (responseCode >= 300 && responseCode < 400);
|
||||
|
||||
// Checks for HSTS Header
|
||||
hstsHeaderList.add(detectHSTSHeader(url, connection));
|
||||
|
||||
// Handle redirects
|
||||
if (redirected) {
|
||||
// Location header contains the URL to redirect to
|
||||
String newUrl = connection.getHeaderField("Location");
|
||||
if (newUrl == null) {
|
||||
break;
|
||||
}
|
||||
URI newUri = uri.resolve(newUrl);
|
||||
// check for SSL stripping during redirect
|
||||
sslStrippingList.add(checkRedirectForSSLStripping(uri, newUri));
|
||||
|
||||
// Handle relative URLs
|
||||
uri = uri.resolve(newUrl);
|
||||
url = uri.toURL();
|
||||
redirectChain.add(url.toString());
|
||||
redirectCount++;
|
||||
logger.info("Redirect #{}: {}",redirectCount, newUrl);
|
||||
} else {
|
||||
// No redirect, so no SSL stripping
|
||||
sslStrippingList.add(false);
|
||||
}
|
||||
|
||||
connection.disconnect();
|
||||
} while (redirected && redirectCount < MAX_REDIRECT_COUNT);
|
||||
|
||||
details.setRedirect(redirectChain.size() - 1);
|
||||
details.setRedirectChain(redirectChain);
|
||||
details.setSslStripping(sslStrippingList);
|
||||
details.setHstsHeader(hstsHeaderList);
|
||||
} catch (URISyntaxException e){
|
||||
logger.error("Error in breaking down URL: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
// Function to check if the redirect is from HTTPS to HTTP
|
||||
private boolean checkRedirectForSSLStripping(URI originalUri, URI newUri) {
|
||||
return originalUri.getScheme().equalsIgnoreCase("https") &&
|
||||
newUri.getScheme().equalsIgnoreCase("http");
|
||||
}
|
||||
// Function to check if HSTS header is present for HTTPS connections
|
||||
private String detectHSTSHeader(URL url, HttpURLConnection connection) {
|
||||
if (connection instanceof HttpsURLConnection) {
|
||||
String hstsHeader = connection.getHeaderField("Strict-Transport-Security");
|
||||
if (hstsHeader != null && !hstsHeader.isEmpty()) {
|
||||
logger.info("HSTS Header detected for {}: {}", url, hstsHeader);
|
||||
return INFO_HSTS_HEADER_PREFIX + hstsHeader;
|
||||
} else {
|
||||
logger.warn("No HSTS Header for HTTPS connection to {}", url);
|
||||
return INFO_NO_HSTS_HEADER;
|
||||
}
|
||||
}
|
||||
return INFO_NON_SECURE_CONNECTION;
|
||||
}
|
||||
|
||||
public URLVerificationResponse verifyURL(QRCodePayload payload) {
|
||||
URLVerificationResponse response = new URLVerificationResponse();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
package com.safeqr.app.qrcode.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.safeqr.app.qrcode.dto.QRCodePayload;
|
||||
import com.safeqr.app.qrcode.dto.request.QRCodePayload;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.safeqr.app.qrcode.service;
|
||||
|
||||
import com.safeqr.app.exceptions.ResourceNotFoundExceptions;
|
||||
import com.safeqr.app.qrcode.entity.WifiEntity;
|
||||
import com.safeqr.app.qrcode.repository.WifiRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
public class WifiVerificationService {
|
||||
private final WifiRepository wifiRepository;
|
||||
private static final Logger logger = LoggerFactory.getLogger(WifiVerificationService.class);
|
||||
|
||||
@Autowired
|
||||
public WifiVerificationService(WifiRepository wifiRepository) {
|
||||
this.wifiRepository = wifiRepository;
|
||||
}
|
||||
public WifiEntity getWifiEntityByQRCodeId(UUID qrCodeId) {
|
||||
logger.info("qrCodeId retrieving: {}", qrCodeId);
|
||||
return wifiRepository.findByQrCodeId(qrCodeId)
|
||||
.orElseThrow(() -> new ResourceNotFoundExceptions("Wifi not found for QR Code id: " + qrCodeId));
|
||||
}
|
||||
public void insertDB(WifiEntity wifiEntity) {
|
||||
wifiRepository.save(wifiEntity);
|
||||
}
|
||||
|
||||
}
|
||||
BIN
src/main/java/com/safeqr/app/user/.DS_Store
vendored
BIN
src/main/java/com/safeqr/app/user/.DS_Store
vendored
Binary file not shown.
@@ -1,25 +1,85 @@
|
||||
package com.safeqr.app.user.controller;
|
||||
|
||||
import static com.safeqr.app.constants.APIConstants.*;
|
||||
import static com.safeqr.app.constants.CommonConstants.HEADER_USER_ID;
|
||||
|
||||
import com.safeqr.app.qrcode.entity.QRCodeEntity;
|
||||
import com.safeqr.app.user.dto.BaseResponse;
|
||||
import com.safeqr.app.user.dto.BookmarkRequestDto;
|
||||
import com.safeqr.app.user.dto.ScannedHistoriesDto;
|
||||
import com.safeqr.app.user.dto.UserResponseDto;
|
||||
import com.safeqr.app.user.service.UserService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/v1")
|
||||
@RequestMapping(API_VERSION)
|
||||
public class UserController {
|
||||
@Autowired
|
||||
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
|
||||
UserService userService;
|
||||
@Autowired
|
||||
public UserController(UserService userService) {
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@GetMapping(value = "/version", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<Map<String, String>> version() {
|
||||
System.out.println(userService.getUserByEmail());
|
||||
System.out.println("Health Check");
|
||||
logger.info("Health Check");
|
||||
return ResponseEntity.ok(Map.of("version","SafeQR v1.0.2"));
|
||||
}
|
||||
|
||||
@GetMapping(value = API_URL_USER_GET, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<UserResponseDto> getUser(@RequestHeader(name = HEADER_USER_ID) String userId) {
|
||||
logger.info("Invoking GET User endpoint");
|
||||
return ResponseEntity.ok(userService.getUserById(userId));
|
||||
}
|
||||
|
||||
@GetMapping(value = API_URL_USER_GET_SCANNED_HISTORIES, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<List<ScannedHistoriesDto>> getUserScannedHistories(@RequestHeader(name = HEADER_USER_ID) String userId) {
|
||||
logger.info("Invoking GET User Scanned Histories endpoint");
|
||||
return ResponseEntity.ok(userService.getUserScannedHistories(userId));
|
||||
}
|
||||
|
||||
@PutMapping(value = API_URL_USER_DELETE_SCANNED_HISTORIES, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<BaseResponse> deleteScannedHistory(@RequestHeader(name = HEADER_USER_ID) String userId, @RequestBody BookmarkRequestDto bookmarkRequestDto) {
|
||||
logger.info("Invoking PUT Delete Single Scanned History endpoint");
|
||||
return ResponseEntity.ok(userService.deleteScannedHistory(userId, bookmarkRequestDto.getQrCodeId()));
|
||||
}
|
||||
|
||||
@PutMapping(value = API_URL_USER_DELETE_ALL_SCANNED_HISTORIES, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<BaseResponse> deleteAllScannedHistories(@RequestHeader(name = HEADER_USER_ID) String userId) {
|
||||
logger.info("Invoking PUT Delete All Scanned Histories endpoint");
|
||||
return ResponseEntity.ok(userService.deleteAllScannedHistoriesByUserId(userId));
|
||||
}
|
||||
|
||||
@GetMapping(value = API_URL_USER_GET_BOOKMARKS, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<List<ScannedHistoriesDto>> getUserBookmarks(@RequestHeader(name = HEADER_USER_ID) String userId) {
|
||||
logger.info("Invoking GET User bookmarks endpoint");
|
||||
return ResponseEntity.ok(userService.getUserBookmarks(userId));
|
||||
}
|
||||
|
||||
@PostMapping(value = API_URL_USER_SET_BOOKMARK, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<BaseResponse> setBookmark(@RequestHeader(name = HEADER_USER_ID) String userId, @RequestBody BookmarkRequestDto bookmarkRequestDto) {
|
||||
logger.info("Invoking POST User bookmark endpoint");
|
||||
return ResponseEntity.ok(userService.setBookmark(userId, bookmarkRequestDto.getQrCodeId()));
|
||||
}
|
||||
|
||||
@PutMapping(value = API_URL_USER_DELETE_BOOKMARK, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<BaseResponse> deleteBookmark(@RequestHeader(name = HEADER_USER_ID) String userId, @RequestBody BookmarkRequestDto bookmarkRequestDto) {
|
||||
logger.info("Invoking PUT Delete Single Bookmark endpoint");
|
||||
return ResponseEntity.ok(userService.deleteBookmark(userId, bookmarkRequestDto.getQrCodeId()));
|
||||
}
|
||||
|
||||
@PutMapping(value = API_URL_USER_DELETE_ALL_BOOKMARK, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<BaseResponse> deleteAllBookmark(@RequestHeader(name = HEADER_USER_ID) String userId) {
|
||||
logger.info("Invoking PUT Delete All Bookmark endpoint");
|
||||
return ResponseEntity.ok(userService.deleteAllBookmarkByUserId(userId));
|
||||
}
|
||||
}
|
||||
|
||||
11
src/main/java/com/safeqr/app/user/dto/BaseResponse.java
Normal file
11
src/main/java/com/safeqr/app/user/dto/BaseResponse.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package com.safeqr.app.user.dto;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class BaseResponse {
|
||||
private String message;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.safeqr.app.user.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
public class BookmarkRequestDto {
|
||||
private UUID qrCodeId;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.safeqr.app.user.dto;
|
||||
|
||||
import com.safeqr.app.qrcode.entity.QRCodeEntity;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class ScannedHistoriesDto {
|
||||
|
||||
private QRCodeEntity data;
|
||||
private boolean bookmarked;
|
||||
|
||||
public ScannedHistoriesDto(QRCodeEntity qrCodeEntity, boolean bookmarked) {
|
||||
this.data = qrCodeEntity;
|
||||
this.bookmarked = bookmarked;
|
||||
}
|
||||
}
|
||||
19
src/main/java/com/safeqr/app/user/dto/UserResponseDto.java
Normal file
19
src/main/java/com/safeqr/app/user/dto/UserResponseDto.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package com.safeqr.app.user.dto;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class UserResponseDto {
|
||||
private String id;
|
||||
private String name;
|
||||
private String email;
|
||||
private OffsetDateTime dateJoined;
|
||||
private OffsetDateTime dateUpdated;
|
||||
private List<String> roles;
|
||||
private String status;
|
||||
}
|
||||
@@ -1,18 +1,19 @@
|
||||
package com.safeqr.app.user.entity;
|
||||
|
||||
import io.hypersistence.utils.hibernate.type.array.ListArrayType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
import java.util.List;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
@Entity
|
||||
@Data
|
||||
@Table(name="user", schema = "safeqr")
|
||||
@@ -21,8 +22,15 @@ public class UserEntity {
|
||||
private String id;
|
||||
private String name;
|
||||
private String email;
|
||||
private OffsetDateTime date_created;
|
||||
private OffsetDateTime date_updated;
|
||||
private String source;
|
||||
|
||||
@Column(name = "date_created")
|
||||
private OffsetDateTime dateCreated;
|
||||
|
||||
@Column(name = "date_updated")
|
||||
private OffsetDateTime dateUpdated;
|
||||
|
||||
@Type(ListArrayType.class)
|
||||
@Column(name = "roles", columnDefinition = "text[]")
|
||||
private List<String> roles;
|
||||
private String status;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.safeqr.app.user.model;
|
||||
|
||||
public class CognitoTokenModel {
|
||||
}
|
||||
@@ -6,5 +6,4 @@ import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface UserRepository extends JpaRepository<UserEntity, String> {
|
||||
UserEntity findByEmail(String email);
|
||||
}
|
||||
|
||||
@@ -1,23 +1,119 @@
|
||||
package com.safeqr.app.user.service;
|
||||
|
||||
import com.safeqr.app.exceptions.ResourceAlreadyExists;
|
||||
import com.safeqr.app.exceptions.ResourceNotFoundExceptions;
|
||||
import com.safeqr.app.qrcode.entity.ScanBookmarkEntity;
|
||||
import com.safeqr.app.qrcode.entity.ScanHistoryEntity;
|
||||
import com.safeqr.app.qrcode.repository.ScanBookmarkRepository;
|
||||
import com.safeqr.app.qrcode.repository.ScanHistoryRepository;
|
||||
import com.safeqr.app.user.dto.BaseResponse;
|
||||
import com.safeqr.app.user.dto.ScannedHistoriesDto;
|
||||
import com.safeqr.app.user.dto.UserResponseDto;
|
||||
import com.safeqr.app.user.entity.UserEntity;
|
||||
import com.safeqr.app.user.repository.UserRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
public class UserService {
|
||||
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
public UserService(UserRepository userRepository,
|
||||
ScanHistoryRepository scanHistoryRepository,
|
||||
ScanBookmarkRepository scanBookmarkRepository) {
|
||||
this.userRepository = userRepository;
|
||||
this.scanHistoryRepository = scanHistoryRepository;
|
||||
this.scanBookmarkRepository = scanBookmarkRepository;
|
||||
}
|
||||
private final UserRepository userRepository;
|
||||
private final ScanHistoryRepository scanHistoryRepository;
|
||||
private final ScanBookmarkRepository scanBookmarkRepository;
|
||||
|
||||
public String getUserByEmail() {
|
||||
public UserResponseDto getUserById(String userId) {
|
||||
// Find user by id
|
||||
UserEntity userEntity = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new ResourceNotFoundExceptions("User id not found: " + userId));
|
||||
|
||||
// Retrieve the user by email
|
||||
UserEntity retrievedUser = userRepository.findByEmail("piggyinu@gmail.com");
|
||||
if (retrievedUser != null) {
|
||||
return "User found: " + retrievedUser.getName();
|
||||
// Map to DTO before returning to controller
|
||||
return UserResponseDto.builder()
|
||||
.id(userEntity.getId())
|
||||
.email(userEntity.getEmail())
|
||||
.name(userEntity.getName())
|
||||
.dateJoined(userEntity.getDateCreated())
|
||||
.dateUpdated(userEntity.getDateUpdated())
|
||||
.roles(userEntity.getRoles())
|
||||
.status(userEntity.getStatus())
|
||||
.build();
|
||||
}
|
||||
public List<ScannedHistoriesDto> getUserScannedHistories(String userId) {
|
||||
return scanHistoryRepository.findAllQRCodesByUserId(userId);
|
||||
}
|
||||
@Transactional
|
||||
public BaseResponse deleteScannedHistory(String userId, UUID qrCodeId) {
|
||||
int updatedCount = scanHistoryRepository.updateScannedHistoryToInactive(userId, qrCodeId);
|
||||
// throw exception if bookmark not found
|
||||
if (updatedCount < 1)
|
||||
throw new ResourceNotFoundExceptions("Scanned QR Code not found");
|
||||
|
||||
return BaseResponse.builder().message("Scanned QR Code deleted successfully").build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public BaseResponse deleteAllScannedHistoriesByUserId(String userId) {
|
||||
int updatedCount = scanHistoryRepository.updateScannedHistoriesToInactiveByUserId(userId);
|
||||
|
||||
return (updatedCount < 1) ?
|
||||
BaseResponse.builder().message("No QR Code not found").build():
|
||||
BaseResponse.builder().message("All scanned QR Code deleted successfully").build();
|
||||
}
|
||||
public List<ScannedHistoriesDto> getUserBookmarks(String userId) {
|
||||
return scanHistoryRepository.findAllBookmarksByUserId(userId);
|
||||
}
|
||||
@Transactional
|
||||
public BaseResponse setBookmark(String userId, UUID qrCodeId) {
|
||||
// Check if the bookmark already exists
|
||||
Optional<ScanHistoryEntity> existingBookmark = scanHistoryRepository.findByUserIdAndQrCodeId(userId, qrCodeId);
|
||||
|
||||
// throw exception if bookmark already exists
|
||||
if (existingBookmark.isPresent()) {
|
||||
throw new ResourceAlreadyExists("Bookmark already exists!");
|
||||
}
|
||||
return "User not found";
|
||||
|
||||
// Save bookmark by updating booked to true
|
||||
int updatedCount = scanHistoryRepository.updateBookmarkStatusToActive(userId, qrCodeId);
|
||||
|
||||
if (updatedCount < 1)
|
||||
throw new ResourceNotFoundExceptions("Unable to create bookmark. The QR code may not exist.");
|
||||
|
||||
return BaseResponse.builder().message("Bookmark saved successfully").build();
|
||||
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public BaseResponse deleteBookmark(String userId, UUID qrCodeId) {
|
||||
int updatedCount = scanHistoryRepository.updateBookmarkStatusToInactive(userId, qrCodeId);
|
||||
// throw exception if bookmark not found
|
||||
if (updatedCount < 1)
|
||||
throw new ResourceNotFoundExceptions("Bookmark not found");
|
||||
|
||||
return BaseResponse.builder().message("Bookmark deleted successfully").build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public BaseResponse deleteAllBookmarkByUserId(String userId) {
|
||||
int updatedCount = scanHistoryRepository.updateBookmarkStatusToInactiveByUserId(userId);
|
||||
|
||||
return (updatedCount < 1) ?
|
||||
BaseResponse.builder().message("No Bookmark not found").build():
|
||||
BaseResponse.builder().message("All Bookmarks deleted successfully").build();
|
||||
}
|
||||
}
|
||||
|
||||
BIN
src/main/resources/.DS_Store
vendored
BIN
src/main/resources/.DS_Store
vendored
Binary file not shown.
@@ -1,21 +0,0 @@
|
||||
http.port=${HTTP_PORT}
|
||||
server.port=${SERVER_PORT}
|
||||
server.ssl.enabled=true
|
||||
server.ssl.key-store-type=${SERVER_SSL_KEY_STORE_TYPE}
|
||||
server.ssl.key-store=${SERVER_SSL_KEY_STORE_LOCATION}
|
||||
server.ssl.key-store-password=${SERVER_SSL_KEY_STORE_PASSWORD}
|
||||
server.ssl.key-alias=${SERVER_SSL_KEY_ALIAS}
|
||||
trust.store=${SERVER_SSL_TRUST_STORE_LOCATION}
|
||||
trust.store.password=${SERVER_SSL_TRUST_STORE_PASSWORD}
|
||||
|
||||
spring.datasource.url=${SERVER_DB_URL}
|
||||
spring.datasource.username=${SERVER_DB_USERNAME}
|
||||
spring.datasource.password=${SERVER_DB_PASSWORD}
|
||||
spring.datasource.driver-class-name=${SERVER_DB_DRIVER_CLASS_NAME}
|
||||
#spring.jpa.database-platform=${SERVER_DB_DIALECT}
|
||||
spring.jpa.hibernate.ddl-auto=update
|
||||
spring.jpa.show-sql=true
|
||||
spring.jpa.properties.hibernate.format_sql=true
|
||||
|
||||
google.safebrowsing.api.key=${GOOGLE_SAFE_BROWSING_API_KEY}
|
||||
virustotal.api.key=${VIRUSTOTAL_API_KEY}
|
||||
@@ -1,2 +1,35 @@
|
||||
spring.application.name=SafeQR-app
|
||||
spring.profiles.active=prod
|
||||
spring.profiles.active=local
|
||||
#http.port=${HTTP_PORT}
|
||||
#server.port=${SERVER_PORT}
|
||||
#server.ssl.enabled=true
|
||||
#server.ssl.key-store-type=${SERVER_SSL_KEY_STORE_TYPE}
|
||||
#server.ssl.key-store=${SERVER_SSL_KEY_STORE_LOCATION}
|
||||
#server.ssl.key-store-password=${SERVER_SSL_KEY_STORE_PASSWORD}
|
||||
#server.ssl.key-alias=${SERVER_SSL_KEY_ALIAS}
|
||||
#trust.store=${SERVER_SSL_TRUST_STORE_LOCATION}
|
||||
#trust.store.password=${SERVER_SSL_TRUST_STORE_PASSWORD}
|
||||
|
||||
spring.datasource.url=${SERVER_DB_URL}
|
||||
spring.datasource.username=${SERVER_DB_USERNAME}
|
||||
spring.datasource.password=${SERVER_DB_PASSWORD}
|
||||
spring.datasource.driver-class-name=${SERVER_DB_DRIVER_CLASS_NAME}
|
||||
#spring.jpa.database-platform=${SERVER_DB_DIALECT}
|
||||
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
|
||||
spring.jpa.hibernate.ddl-auto=none
|
||||
spring.jpa.show-sql=true
|
||||
spring.jpa.properties.hibernate.format_sql=true
|
||||
|
||||
google.safebrowsing.api.key=${GOOGLE_SAFE_BROWSING_API_KEY}
|
||||
virustotal.api.key=${VIRUSTOTAL_API_KEY}
|
||||
|
||||
#Gmail API Configuration
|
||||
gmail.client.clientId={GOOGLE_CLIENT_ID}
|
||||
gmail.client.clientSecret={GOOGLE_CLIENT_SECRET}
|
||||
gmail.client.accessTokenUri=https://accounts.google.com/o/oauth2/token
|
||||
gmail.client.userAuthorizationUri=https://accounts.google.com/o/oauth2/auth
|
||||
gmail.client.clientAuthenticationScheme=query
|
||||
gmail.client.scope=https://www.googleapis.com/auth/gmail.readonly
|
||||
gmail.resource.userInfoUri=https://www.googleapis.com/gmail/v1/users/me/profile
|
||||
gmail.resource.preferTokenInfo=true
|
||||
gmail.client.redirectUri=https://bk5wiynzsi.execute-api.ap-southeast-1.amazonaws.com/api/gmail/callback
|
||||
BIN
src/main/resources/keystore/.DS_Store
vendored
BIN
src/main/resources/keystore/.DS_Store
vendored
Binary file not shown.
55
src/main/resources/keystore/safeqr.crt
Normal file
55
src/main/resources/keystore/safeqr.crt
Normal file
@@ -0,0 +1,55 @@
|
||||
Bag Attributes
|
||||
friendlyName: safeqr
|
||||
localKeyID: 54 69 6D 65 20 31 37 31 38 35 31 32 38 36 35 30 37 39
|
||||
subject=C=SG, ST=SG, L=Singapore, O=UOW, OU=IT, CN=SafeQR App
|
||||
issuer=C=SG, ST=SG, L=Singapore, O=UOW, OU=IT, CN=SafeQR App
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDXzCCAkegAwIBAgIId40EXlih454wDQYJKoZIhvcNAQEMBQAwXjELMAkGA1UE
|
||||
BhMCU0cxCzAJBgNVBAgTAlNHMRIwEAYDVQQHEwlTaW5nYXBvcmUxDDAKBgNVBAoT
|
||||
A1VPVzELMAkGA1UECxMCSVQxEzARBgNVBAMTClNhZmVRUiBBcHAwHhcNMjQwNjE2
|
||||
MDQ0MTA0WhcNMzQwNjE0MDQ0MTA0WjBeMQswCQYDVQQGEwJTRzELMAkGA1UECBMC
|
||||
U0cxEjAQBgNVBAcTCVNpbmdhcG9yZTEMMAoGA1UEChMDVU9XMQswCQYDVQQLEwJJ
|
||||
VDETMBEGA1UEAxMKU2FmZVFSIEFwcDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
|
||||
AQoCggEBALHaMAOaBuX2D9Gq6islUVOKQr343FOHilQiNG7H4Pm43flr4MbdvEaa
|
||||
Dcqi5tWufssoWnvV0cVCwFGvMnWWt/21/LV76jd9XhQdY6tDJZfdccgRco8LS8VL
|
||||
G30d2NhcUbMVH//wsLcvoIkic4w++0SjeoKa5qnS4VX2dkI3fpOYJUMlRogIm1uV
|
||||
Rlv6jcUK3uYE734lHulwB/ggtmbG2ulcxgOsW4O3HTafU6wZIC8pwfBjbpUnrFc7
|
||||
xcIM2VGHkmaP1vyIRbOT5TWMDMlNP+yR/abA8EJX6VqUE08ENen5G66DfzoAeuiE
|
||||
UwY7OwIbXfuQR97RszAx6vnMQfKak30CAwEAAaMhMB8wHQYDVR0OBBYEFJ1MqzAY
|
||||
b2qwbXEmZcAYEGyZ6EVNMA0GCSqGSIb3DQEBDAUAA4IBAQBFZH7Gp/PiZOpubsi1
|
||||
oVrZYLU6yAucq+RSreO2YrCMJtqiPtYxhB84kaQ277u4BW4xsDjo7mxh1qwEZvSA
|
||||
9K13Pu3FgilzdJnm5E54jLSvmW62gIjziuMg34ioVh6WnD4/Tre5vHRcR2IHYghh
|
||||
5rI6gFiSHvjHun71xoeaUdJQUjx4LFDP5cA5U3oGcN08yMopja9ABQIUAF5MWg3U
|
||||
VDN74JRkae4VIXQ/4gbuDwjmU6vyJHKNxrFxbtJCvmSBLdCNONcEHiHkKb4SIx4C
|
||||
QwOSuhRc8bqWepfLgCqskCVqEJW1s2lK2swEN3gM7D89jtuHOG/itJH6J1+COgea
|
||||
dwSP
|
||||
-----END CERTIFICATE-----
|
||||
Bag Attributes
|
||||
friendlyName: safeqr-nlb-cert
|
||||
localKeyID: 54 69 6D 65 20 31 37 31 39 37 39 33 35 39 34 36 33 32
|
||||
subject=C=SG, ST=SG, L=Singapore, O=UOW, OU=IT, CN=safeqr-nlb-6bd79c7ba50f3cb5.elb.ap-southeast-1.amazonaws.com
|
||||
issuer=C=SG, ST=SG, L=Singapore, O=UOW, OU=IT, CN=safeqr-nlb-6bd79c7ba50f3cb5.elb.ap-southeast-1.amazonaws.com
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEAzCCAuugAwIBAgIUMBgs2njKVCOQNmrQZmtHE04JYwgwDQYJKoZIhvcNAQEL
|
||||
BQAwgZAxCzAJBgNVBAYTAlNHMQswCQYDVQQIDAJTRzESMBAGA1UEBwwJU2luZ2Fw
|
||||
b3JlMQwwCgYDVQQKDANVT1cxCzAJBgNVBAsMAklUMUUwQwYDVQQDDDxzYWZlcXIt
|
||||
bmxiLTZiZDc5YzdiYTUwZjNjYjUuZWxiLmFwLXNvdXRoZWFzdC0xLmFtYXpvbmF3
|
||||
cy5jb20wHhcNMjQwNjMwMTMzNjA0WhcNMjUwNjMwMTMzNjA0WjCBkDELMAkGA1UE
|
||||
BhMCU0cxCzAJBgNVBAgMAlNHMRIwEAYDVQQHDAlTaW5nYXBvcmUxDDAKBgNVBAoM
|
||||
A1VPVzELMAkGA1UECwwCSVQxRTBDBgNVBAMMPHNhZmVxci1ubGItNmJkNzljN2Jh
|
||||
NTBmM2NiNS5lbGIuYXAtc291dGhlYXN0LTEuYW1hem9uYXdzLmNvbTCCASIwDQYJ
|
||||
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAO08NbCn1ANY/JCrxtqR2LZRovSbGaI1
|
||||
nVQalLSfQolEJT9J1bP5haCf002us5Zw5+zgfhcbkNgyom0vakmU6I1n9iMZyVQx
|
||||
sb8MJfkJlB/vR6VPSAowrvWy344aPhsQJkxERGOHcuIfaZroLhqdwoYl1lTLz5AD
|
||||
5lt266AwiUY4GehEIZUo5fgdoueMqCjnPD7lH2nALS9DH0R9uGh/urZJJ40yYkSi
|
||||
MKzEStYR0qdmoIipNW7u3uBiqbulbVlhdgXABRrsWgxWaDH40UvJss0/a5ojmxxU
|
||||
W5JQJCKyF150Xr6UoE8XCsd1XptVDbsMy//xKkkuns0m3t24aOw1S8cCAwEAAaNT
|
||||
MFEwHQYDVR0OBBYEFE+wiD0lcUAnJ4ITiEQsBmQjobehMB8GA1UdIwQYMBaAFE+w
|
||||
iD0lcUAnJ4ITiEQsBmQjobehMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
|
||||
BQADggEBAKjjNVMxEsjk4ivdjC6qbmEXJogSofw5We2GhcJyria/r2u9k65vHEk1
|
||||
/T79lF2H8IvqNpSAnuyKroJBXD16p+ZOOeUQu8oCudl4iFT2T2DMP102AwP9L902
|
||||
vaHqBobhTCkG++ZzefmnpMyaa7hrVFnW9iP4vmHhM4yqR5ThIEvofVJUbJHhGug4
|
||||
gDBRW6xV0zaVp+thFXmGLXuURilQTfCen2WVL8DndjQxMc+jobCN4vxSAxR23Ndo
|
||||
Zm5/a5SnxXypcpjXnI17gzvybpZrCD/NZcNmpnZw8vf5m7BC9C5uX4CGujkto7R5
|
||||
mY4JDDt0XlP7hiGlHMkr/IUaVARoocE=
|
||||
-----END CERTIFICATE-----
|
||||
BIN
src/test/.DS_Store
vendored
BIN
src/test/.DS_Store
vendored
Binary file not shown.
BIN
src/test/java/.DS_Store
vendored
BIN
src/test/java/.DS_Store
vendored
Binary file not shown.
Reference in New Issue
Block a user