Cloud Native Impl
This commit is contained in:
parent
ff528b7017
commit
16f4a85c40
58 changed files with 5834 additions and 321054 deletions
47
Dockerfile
Normal file
47
Dockerfile
Normal file
|
@ -0,0 +1,47 @@
|
|||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Stage 1: Build the application
|
||||
FROM maven:3.9.6-eclipse-temurin-21 AS builder
|
||||
|
||||
# Clone the repository
|
||||
RUN git clone https://github.com/aamitn/URLShortener.git
|
||||
|
||||
WORKDIR /URLShortener
|
||||
|
||||
# Build the application
|
||||
RUN mvn clean install
|
||||
|
||||
# Stage 2: Create the final image
|
||||
FROM tomcat:10-jdk21-openjdk-slim
|
||||
|
||||
# Set environment variables
|
||||
ENV CATALINA_BASE /usr/local/tomcat
|
||||
ENV CATALINA_HOME /usr/local/tomcat
|
||||
ENV PATH $CATALINA_HOME/bin:$PATH
|
||||
|
||||
# Copy the WAR file from the builder stage
|
||||
COPY target/shortener.war $CATALINA_BASE/webapps/
|
||||
|
||||
|
||||
# Add configuration for document base path
|
||||
COPY server.xml $CATALINA_BASE/conf/server.xml
|
||||
|
||||
|
||||
# Expose ports
|
||||
EXPOSE 8080
|
||||
EXPOSE 3306
|
||||
|
||||
|
||||
# Copy the startup script
|
||||
COPY shortener.sh /usr/local/tomcat/shortener.sh
|
||||
|
||||
# Copy the sql file
|
||||
COPY create.sql /usr/local/tomcat/create.sql
|
||||
|
||||
# Grant execute permissions to the startup.sh script
|
||||
RUN chmod +x /usr/local/tomcat/shortener.sh
|
||||
|
||||
# Start Tomcat and MariaDB using the startup script
|
||||
CMD ["sh", "/usr/local/tomcat/shortener.sh"]
|
||||
|
||||
|
3
META-INF/MANIFEST.MF
Normal file
3
META-INF/MANIFEST.MF
Normal file
|
@ -0,0 +1,3 @@
|
|||
Manifest-Version: 1.0
|
||||
Main-Class: com.bitmutex.shortener.UrlShortenerApplication
|
||||
|
815
create.sql
815
create.sql
File diff suppressed because one or more lines are too long
10
docker-compose.yml
Normal file
10
docker-compose.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
version: '3.8'
|
||||
|
||||
services:
|
||||
shortener-app:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "8080:8080"
|
||||
- "3306:3306"
|
306473
logs/shortener.log
306473
logs/shortener.log
File diff suppressed because it is too large
Load diff
BIN
out/artifacts/shortener_jar/shortener.jar
Normal file
BIN
out/artifacts/shortener_jar/shortener.jar
Normal file
Binary file not shown.
100
pom.xml
100
pom.xml
|
@ -25,20 +25,17 @@
|
|||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--QRCODE -->
|
||||
<dependency>
|
||||
<groupId>com.google.zxing</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>3.4.1</version> <!-- Replace with the latest version -->
|
||||
<version>3.4.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.zxing</groupId>
|
||||
<artifactId>javase</artifactId>
|
||||
<version>3.4.1</version> <!-- Replace with the latest version -->
|
||||
<version>3.4.1</version>
|
||||
</dependency>
|
||||
<!--QRCODE -->
|
||||
<dependency>
|
||||
|
@ -72,6 +69,7 @@
|
|||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
|
@ -82,7 +80,7 @@
|
|||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt</artifactId>
|
||||
<version>0.9.1</version> <!-- or the latest version -->
|
||||
<version>0.9.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
@ -141,7 +139,7 @@
|
|||
<dependency>
|
||||
<groupId>org.testng</groupId>
|
||||
<artifactId>testng</artifactId>
|
||||
<version>7.4.0</version>
|
||||
<version>7.9.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
@ -157,23 +155,35 @@
|
|||
<version>2.1.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<!-- Set packaging to WAR -->
|
||||
<packaging>${project.packaging}</packaging>
|
||||
|
||||
<build>
|
||||
<finalName>${artifactId}</finalName>
|
||||
|
||||
<pluginManagement>
|
||||
|
||||
<plugins>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
|
@ -183,7 +193,14 @@
|
|||
<suiteXmlFiles>
|
||||
<suiteXmlFile>testng.xml</suiteXmlFile>
|
||||
</suiteXmlFiles>
|
||||
<properties>
|
||||
<property>
|
||||
<name>surefire.testng.verbose</name>
|
||||
<value>10</value>
|
||||
</property>
|
||||
</properties>
|
||||
</configuration>
|
||||
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springdoc</groupId>
|
||||
|
@ -198,10 +215,20 @@
|
|||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>2.3.3.RELEASE</version>
|
||||
<version>3.2.2</version>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>pre-integration-test</id>
|
||||
|
@ -217,6 +244,37 @@
|
|||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
|
||||
|
||||
<profiles>
|
||||
|
||||
<profile>
|
||||
<id>dev</id>
|
||||
<activation>
|
||||
<activeByDefault>false</activeByDefault>
|
||||
</activation>
|
||||
<properties>
|
||||
<project.packaging>jar</project.packaging>
|
||||
</properties>
|
||||
</profile>
|
||||
|
||||
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
<properties>
|
||||
<project.packaging>war</project.packaging>
|
||||
</properties>
|
||||
</profile>
|
||||
|
||||
</profiles>
|
||||
|
||||
|
||||
</project>
|
||||
|
|
161
server.xml
Normal file
161
server.xml
Normal file
|
@ -0,0 +1,161 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<!-- Note: A "Server" is not itself a "Container", so you may not
|
||||
define subcomponents such as "Valves" at this level.
|
||||
Documentation at /docs/config/server.html
|
||||
-->
|
||||
<Server port="8005" shutdown="SHUTDOWN">
|
||||
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
|
||||
<!-- Security listener. Documentation at /docs/config/listeners.html
|
||||
<Listener className="org.apache.catalina.security.SecurityListener" />
|
||||
-->
|
||||
<!-- APR library loader. Documentation at /docs/apr.html -->
|
||||
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
|
||||
<!-- Prevent memory leaks due to use of particular java/javax APIs-->
|
||||
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
|
||||
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
|
||||
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
|
||||
|
||||
<!-- Global JNDI resources
|
||||
Documentation at /docs/jndi-resources-howto.html
|
||||
-->
|
||||
<GlobalNamingResources>
|
||||
<!-- Editable user database that can also be used by
|
||||
UserDatabaseRealm to authenticate users
|
||||
-->
|
||||
<Resource name="UserDatabase" auth="Container"
|
||||
type="org.apache.catalina.UserDatabase"
|
||||
description="User database that can be updated and saved"
|
||||
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
|
||||
pathname="conf/tomcat-users.xml" />
|
||||
</GlobalNamingResources>
|
||||
|
||||
<!-- A "Service" is a collection of one or more "Connectors" that share
|
||||
a single "Container" Note: A "Service" is not itself a "Container",
|
||||
so you may not define subcomponents such as "Valves" at this level.
|
||||
Documentation at /docs/config/service.html
|
||||
-->
|
||||
<Service name="Catalina">
|
||||
|
||||
<!--The connectors can use a shared executor, you can define one or more named thread pools-->
|
||||
<!--
|
||||
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
|
||||
maxThreads="150" minSpareThreads="4"/>
|
||||
-->
|
||||
|
||||
|
||||
<!-- A "Connector" represents an endpoint by which requests are received
|
||||
and responses are returned. Documentation at :
|
||||
HTTP Connector: /docs/config/http.html
|
||||
AJP Connector: /docs/config/ajp.html
|
||||
Define a non-SSL/TLS HTTP/1.1 Connector on port 8080
|
||||
-->
|
||||
<Connector port="8080" protocol="HTTP/1.1"
|
||||
connectionTimeout="20000"
|
||||
redirectPort="8443"
|
||||
maxParameterCount="1000"
|
||||
/>
|
||||
<!-- A "Connector" using the shared thread pool-->
|
||||
<!--
|
||||
<Connector executor="tomcatThreadPool"
|
||||
port="8080" protocol="HTTP/1.1"
|
||||
connectionTimeout="20000"
|
||||
redirectPort="8443"
|
||||
maxParameterCount="1000"
|
||||
/>
|
||||
-->
|
||||
<!-- Define an SSL/TLS HTTP/1.1 Connector on port 8443 with HTTP/2
|
||||
This connector uses the NIO implementation. The default
|
||||
SSLImplementation will depend on the presence of the APR/native
|
||||
library and the useOpenSSL attribute of the AprLifecycleListener.
|
||||
Either JSSE or OpenSSL style configuration may be used regardless of
|
||||
the SSLImplementation selected. JSSE style configuration is used below.
|
||||
-->
|
||||
<!--
|
||||
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
|
||||
maxThreads="150" SSLEnabled="true"
|
||||
maxParameterCount="1000"
|
||||
>
|
||||
<UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
|
||||
<SSLHostConfig>
|
||||
<Certificate certificateKeystoreFile="conf/localhost-rsa.jks"
|
||||
type="RSA" />
|
||||
</SSLHostConfig>
|
||||
</Connector>
|
||||
-->
|
||||
|
||||
<!-- Define an AJP 1.3 Connector on port 8009 -->
|
||||
<!--
|
||||
<Connector protocol="AJP/1.3"
|
||||
address="::1"
|
||||
port="8009"
|
||||
redirectPort="8443"
|
||||
maxParameterCount="1000"
|
||||
/>
|
||||
-->
|
||||
|
||||
<!-- An Engine represents the entry point (within Catalina) that processes
|
||||
every request. The Engine implementation for Tomcat stand alone
|
||||
analyzes the HTTP headers included with the request, and passes them
|
||||
on to the appropriate Host (virtual host).
|
||||
Documentation at /docs/config/engine.html -->
|
||||
|
||||
<!-- You should set jvmRoute to support load-balancing via AJP ie :
|
||||
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
|
||||
-->
|
||||
<Engine name="Catalina" defaultHost="localhost">
|
||||
|
||||
<!--For clustering, please take a look at documentation at:
|
||||
/docs/cluster-howto.html (simple how to)
|
||||
/docs/config/cluster.html (reference documentation) -->
|
||||
<!--
|
||||
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
|
||||
-->
|
||||
|
||||
<!-- Use the LockOutRealm to prevent attempts to guess user passwords
|
||||
via a brute-force attack -->
|
||||
<Realm className="org.apache.catalina.realm.LockOutRealm">
|
||||
<!-- This Realm uses the UserDatabase configured in the global JNDI
|
||||
resources under the key "UserDatabase". Any edits
|
||||
that are performed against this UserDatabase are immediately
|
||||
available for use by the Realm. -->
|
||||
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
|
||||
resourceName="UserDatabase"/>
|
||||
</Realm>
|
||||
|
||||
<Host name="localhost" appBase="webapps"
|
||||
unpackWARs="true" autoDeploy="true" deployOnStartup="false">
|
||||
|
||||
<!-- SingleSignOn valve, share authentication between web applications
|
||||
Documentation at: /docs/config/valve.html -->
|
||||
<!--
|
||||
<Valve className="org.apache.catalina.authenticator.SingleSignOn" />
|
||||
-->
|
||||
<Context path="" docBase="shortener" debug="0" reloadable="true"></Context>
|
||||
|
||||
<!-- Access log processes all example.
|
||||
Documentation at: /docs/config/valve.html
|
||||
Note: The pattern used is equivalent to using pattern="common" -->
|
||||
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
|
||||
prefix="localhost_access_log" suffix=".txt"
|
||||
pattern="%h %l %u %t "%r" %s %b" />
|
||||
|
||||
</Host>
|
||||
</Engine>
|
||||
</Service>
|
||||
</Server>
|
37
shortener.sh
Normal file
37
shortener.sh
Normal file
|
@ -0,0 +1,37 @@
|
|||
#!/bin/bash
|
||||
|
||||
echo "Installing MariaDB..."
|
||||
# Install MariaDB
|
||||
apt-get update
|
||||
apt-get install -y mariadb-server
|
||||
|
||||
echo "Starting MariaDB service..."
|
||||
# Start MariaDB service
|
||||
service mariadb start
|
||||
|
||||
echo "Waiting for MariaDB to start (adjust sleep time as needed)..."
|
||||
|
||||
# Wait for MariaDB to start
|
||||
while ! mysqladmin ping -hlocalhost -uroot -p'YOUR_PASSWORD' --silent; do
|
||||
echo "MariaDB is not yet available. Waiting..."
|
||||
sleep 5
|
||||
done
|
||||
|
||||
echo "Running SQL script to initialize the database..."
|
||||
# Access MySQL Command Line and run SQL script to initialize the database
|
||||
mysql -u root -e "source /usr/local/tomcat/create.sql"
|
||||
|
||||
echo "Displaying databases and tables..."
|
||||
# Display the databases and tables
|
||||
mysql -u root -e "SHOW DATABASES; USE shortener; SHOW TABLES;"
|
||||
|
||||
echo "Altering user and reloading privileges..."
|
||||
# Run SQL commands to alter user and reload privileges
|
||||
mysql -u root <<EOF
|
||||
ALTER USER 'root'@'localhost' IDENTIFIED VIA mysql_native_password USING PASSWORD('1234qwer');
|
||||
FLUSH PRIVILEGES;
|
||||
EOF
|
||||
|
||||
echo "Starting Tomcat in the background..."
|
||||
# Start Tomcat in the background
|
||||
sh /usr/local/tomcat/bin/catalina.sh run
|
18313
shortener_db_schema.sql
18313
shortener_db_schema.sql
File diff suppressed because it is too large
Load diff
|
@ -2,27 +2,26 @@
|
|||
|
||||
package com.bitmutex.shortener;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Controller
|
||||
public class AnalyticsController {
|
||||
|
||||
@Autowired
|
||||
private AnalyticsService analyticsService;
|
||||
private final AnalyticsService analyticsService;
|
||||
|
||||
private AnalyticsRepository analyticsRepository;
|
||||
|
||||
@Autowired
|
||||
private UrlShortenerService urlShortenerService;
|
||||
private final UrlShortenerService urlShortenerService;
|
||||
|
||||
public AnalyticsController(AnalyticsService analyticsService, UrlShortenerService urlShortenerService) {
|
||||
this.analyticsService = analyticsService;
|
||||
this.urlShortenerService = urlShortenerService;
|
||||
}
|
||||
|
||||
@GetMapping("/api/url/analytics")
|
||||
@ResponseBody
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package com.bitmutex.shortener;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
|
|
@ -2,18 +2,18 @@
|
|||
|
||||
package com.bitmutex.shortener;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class AnalyticsService {
|
||||
|
||||
@Autowired
|
||||
private AnalyticsRepository analyticsRepository;
|
||||
private final AnalyticsRepository analyticsRepository;
|
||||
|
||||
public AnalyticsService(AnalyticsRepository analyticsRepository) {
|
||||
this.analyticsRepository = analyticsRepository;
|
||||
}
|
||||
|
||||
public List<Analytics> findByUrlShortenerId(Long urlShortenerId) {
|
||||
// Implement this method to fetch analytics entries based on urlShortenerId
|
||||
|
|
|
@ -5,9 +5,6 @@ package com.bitmutex.shortener;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Controller
|
||||
|
||||
|
|
|
@ -1,13 +1,7 @@
|
|||
package com.bitmutex.shortener;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
|
||||
|
@ -18,14 +12,18 @@ import java.io.IOException;
|
|||
@RequestMapping("/contact")
|
||||
public class ContactController {
|
||||
|
||||
@Autowired
|
||||
final
|
||||
ContactRepository contactRepository;
|
||||
|
||||
@Autowired
|
||||
private SmsService smsService;
|
||||
private final SmsService smsService;
|
||||
|
||||
@Autowired
|
||||
private EmailService emailService;
|
||||
private final EmailService emailService;
|
||||
|
||||
public ContactController(ContactRepository contactRepository, SmsService smsService, EmailService emailService) {
|
||||
this.contactRepository = contactRepository;
|
||||
this.smsService = smsService;
|
||||
this.emailService = emailService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String showContactForm() {
|
||||
|
|
|
@ -8,7 +8,7 @@ import org.springframework.security.oauth2.core.user.OAuth2User;
|
|||
|
||||
public class CustomOAuth2User implements OAuth2User {
|
||||
|
||||
private OAuth2User oauth2User;
|
||||
private final OAuth2User oauth2User;
|
||||
|
||||
public CustomOAuth2User(OAuth2User oauth2User) {
|
||||
this.oauth2User = oauth2User;
|
||||
|
@ -32,12 +32,12 @@ public class CustomOAuth2User implements OAuth2User {
|
|||
// Additional methods to get custom user details
|
||||
|
||||
public String getEmail() {
|
||||
return (String) oauth2User.getAttribute("email");
|
||||
return oauth2User.getAttribute("email");
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
// Extract first name from GitHub attributes
|
||||
return (String) oauth2User.getAttribute("name");
|
||||
return oauth2User.getAttribute("name");
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package com.bitmutex.shortener;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
|
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
|
||||
|
@ -15,12 +14,17 @@ import java.util.Random;
|
|||
|
||||
@Service
|
||||
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
|
||||
@Autowired
|
||||
final
|
||||
UserRepository userRepository;
|
||||
|
||||
@Autowired
|
||||
final
|
||||
UserService userService;
|
||||
|
||||
public CustomOAuth2UserService(UserRepository userRepository, UserService userService) {
|
||||
this.userRepository = userRepository;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
|
||||
OAuth2User user = super.loadUser(userRequest);
|
||||
|
@ -79,8 +83,7 @@ public class CustomOAuth2UserService extends DefaultOAuth2UserService {
|
|||
|
||||
private UserEntity registerNewOAuth2User(RegistrationRequest request) {
|
||||
// Implement logic to register a new user based on OAuth2 attributes
|
||||
UserEntity newUser = userService.registerNewUser(request);
|
||||
return newUser;
|
||||
return userService.registerNewUser(request);
|
||||
}
|
||||
|
||||
public String randAlphaString() {
|
||||
|
@ -89,12 +92,10 @@ public class CustomOAuth2UserService extends DefaultOAuth2UserService {
|
|||
int targetStringLength = 10;
|
||||
Random random = new Random();
|
||||
|
||||
String generatedString = random.ints(leftLimit, rightLimit + 1)
|
||||
return random.ints(leftLimit, rightLimit + 1)
|
||||
.filter(i -> (i <= 57 || i >= 65) && (i <= 90 || i >= 97))
|
||||
.limit(targetStringLength)
|
||||
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
|
||||
.toString();
|
||||
|
||||
return generatedString;
|
||||
}
|
||||
}
|
|
@ -3,7 +3,6 @@ package com.bitmutex.shortener;
|
|||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
public class CustomUserDetails extends org.springframework.security.core.userdetails.User {
|
||||
private final Long userId;
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
package com.bitmutex.shortener;
|
||||
|
||||
|
||||
import com.nimbusds.openid.connect.sdk.assurance.evidences.attachment.Attachment;
|
||||
import jakarta.mail.MessagingException;
|
||||
import org.checkerframework.checker.units.qual.A;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.mail.javamail.MimeMessageHelper;
|
||||
|
@ -13,12 +10,15 @@ import org.springframework.stereotype.Service;
|
|||
@Service
|
||||
public class EmailService {
|
||||
|
||||
@Autowired
|
||||
private JavaMailSender javaMailSender;
|
||||
private final JavaMailSender javaMailSender;
|
||||
|
||||
@Value("${spring.mail.username}")
|
||||
private String senderEmail;
|
||||
|
||||
public EmailService(JavaMailSender javaMailSender) {
|
||||
this.javaMailSender = javaMailSender;
|
||||
}
|
||||
|
||||
public void sendMail(String to, String subject, String message) {
|
||||
|
||||
try {
|
||||
|
@ -40,7 +40,8 @@ public class EmailService {
|
|||
javaMailSender.send(helper.getMimeMessage());
|
||||
} catch (MessagingException e) {
|
||||
// Handle email sending errors appropriately
|
||||
e.printStackTrace();
|
||||
throw new UrlShortenerException("Email Sending Error",e);
|
||||
// e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,8 +11,6 @@ import org.springframework.web.bind.annotation.GetMapping;
|
|||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
@ -24,13 +22,13 @@ public class ForgotPasswordController {
|
|||
private final UserService userService;
|
||||
|
||||
@Autowired
|
||||
public ForgotPasswordController(UserService userService) {
|
||||
public ForgotPasswordController(UserService userService, JavaMailSender javaMailSender) {
|
||||
this.userService = userService;
|
||||
this.javaMailSender = javaMailSender;
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
private JavaMailSender javaMailSender;
|
||||
private final JavaMailSender javaMailSender;
|
||||
|
||||
@Value("${spring.mail.username}")
|
||||
private String senderEmail;
|
||||
|
|
|
@ -12,9 +12,6 @@ import org.springframework.web.bind.annotation.PostMapping;
|
|||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/forgot-username")
|
||||
public class ForgotUsernameController {
|
||||
|
@ -22,13 +19,13 @@ public class ForgotUsernameController {
|
|||
private final UserService userService;
|
||||
|
||||
@Autowired
|
||||
public ForgotUsernameController(UserService userService) {
|
||||
public ForgotUsernameController(UserService userService, JavaMailSender javaMailSender) {
|
||||
this.userService = userService;
|
||||
this.javaMailSender = javaMailSender;
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
private JavaMailSender javaMailSender;
|
||||
private final JavaMailSender javaMailSender;
|
||||
|
||||
@Value("${spring.mail.username}")
|
||||
private String senderEmail;
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
package com.bitmutex.shortener;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.Optional;
|
||||
|
||||
|
||||
@RequestMapping("/")
|
||||
|
@ -17,8 +12,11 @@ import java.util.Optional;
|
|||
public class HomeController {
|
||||
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
private final UserService userService;
|
||||
|
||||
public HomeController(UserService userService) {
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@GetMapping("/")
|
||||
String home(Authentication authentication, Model model) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package com.bitmutex.shortener;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
@ -16,9 +15,11 @@ import java.util.Map;
|
|||
|
||||
public class ImageController {
|
||||
|
||||
@Autowired
|
||||
private ImageService imageService;
|
||||
private final ImageService imageService;
|
||||
|
||||
public ImageController(ImageService imageService) {
|
||||
this.imageService = imageService;
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/upload")
|
||||
|
@ -51,7 +52,6 @@ public class ImageController {
|
|||
|
||||
@GetMapping("/{imageId}")
|
||||
public ResponseEntity<byte[]> getImageById(@PathVariable Long imageId) {
|
||||
ResponseEntity<byte[]> image = imageService.getImageContent(imageId);
|
||||
return image;
|
||||
return imageService.getImageContent(imageId);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
package com.bitmutex.shortener;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
|
@ -9,18 +8,19 @@ import org.springframework.stereotype.Service;
|
|||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
|
||||
@Service
|
||||
public class ImageServiceImpl implements ImageService {
|
||||
|
||||
@Autowired
|
||||
private ImageRepository imageRepository;
|
||||
private final ImageRepository imageRepository;
|
||||
|
||||
public ImageServiceImpl(ImageRepository imageRepository) {
|
||||
this.imageRepository = imageRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uploadImage(MultipartFile file) {
|
||||
|
@ -46,8 +46,7 @@ public class ImageServiceImpl implements ImageService {
|
|||
return "/api/images/" + savedImage.getId();
|
||||
} catch (IOException e) {
|
||||
// Handle the exception (e.g., log or throw a custom exception)
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
throw new UrlShortenerException("Error uploading image",e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,8 +89,7 @@ public class ImageServiceImpl implements ImageService {
|
|||
return twelveDigitId;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// Handle the exception (e.g., log or throw a custom exception)
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
throw new UrlShortenerException("Error generating unique ID for Image",e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
package com.bitmutex.shortener;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/")
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package com.bitmutex.shortener;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.health.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.info.InfoEndpoint;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
@ -8,8 +7,6 @@ import org.springframework.stereotype.Controller;
|
|||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Controller
|
||||
public class MonitoringController {
|
||||
private final HealthEndpoint healthEndpoint;
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
package com.bitmutex.shortener;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
public class OtpEntity {
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package com.bitmutex.shortener;
|
||||
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
@ -10,9 +8,11 @@ import java.util.Optional;
|
|||
@Service
|
||||
public class OtpService {
|
||||
|
||||
@Autowired
|
||||
private OtpRepository otpRepository;
|
||||
private final OtpRepository otpRepository;
|
||||
|
||||
public OtpService(OtpRepository otpRepository) {
|
||||
this.otpRepository = otpRepository;
|
||||
}
|
||||
|
||||
|
||||
// Generate and store OTP for a given username and phone number
|
||||
|
|
|
@ -1,41 +1,35 @@
|
|||
package com.bitmutex.shortener;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.Principal;
|
||||
import java.util.Optional;
|
||||
|
||||
@RequestMapping("/")
|
||||
@Controller
|
||||
public class ProfileController {
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
private final UserService userService;
|
||||
|
||||
@Autowired
|
||||
private OtpService otpService;
|
||||
private final OtpService otpService;
|
||||
|
||||
@Autowired
|
||||
private SmsService smsService;
|
||||
private final SmsService smsService;
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
private final UserRepository userRepository;
|
||||
|
||||
public ProfileController(UserService userService, OtpService otpService, SmsService smsService, UserRepository userRepository) {
|
||||
this.userService = userService;
|
||||
this.otpService = otpService;
|
||||
this.smsService = smsService;
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
@GetMapping("/profile")
|
||||
public String viewProfile(@RequestParam String username, Model model) {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package com.bitmutex.shortener;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
@ -14,8 +13,11 @@ import java.util.Map;
|
|||
@RequestMapping("/api/user/public/check")
|
||||
public class PublicUserController {
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
private final UserRepository userRepository;
|
||||
|
||||
public PublicUserController(UserRepository userRepository) {
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
@GetMapping("/username")
|
||||
public ResponseEntity<Map<String, Boolean>> checkUsernameExists(@RequestParam String username) {
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
package com.bitmutex.shortener;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
|
@ -17,12 +14,12 @@ import java.time.LocalDateTime;
|
|||
@RequestMapping("/reset-password")
|
||||
public class ResetPasswordController {
|
||||
|
||||
@Autowired
|
||||
private UserService userService; // Assuming you have a UserService
|
||||
private final UserService userService; // Assuming you have a UserService
|
||||
public static PasswordEncoder passwordEncoder;
|
||||
|
||||
public ResetPasswordController(PasswordEncoder passwordEncoder) {
|
||||
public ResetPasswordController(PasswordEncoder passwordEncoder, UserService userService) {
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
|
@ -40,7 +37,7 @@ public class ResetPasswordController {
|
|||
}
|
||||
|
||||
@PostMapping
|
||||
public String processResetPassword(@RequestParam("token") String token, @RequestParam("password") String newPassword, Model model) {
|
||||
public String processResetPassword(@RequestParam("token") String token, @RequestParam("password") String newPassword, Model ignoredModel) {
|
||||
UserEntity user = userService.findByResetToken(token);
|
||||
|
||||
if (user != null && user.getResetTokenExpiryDateTime().isAfter(LocalDateTime.now())) {
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
package com.bitmutex.shortener;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
|
@ -15,15 +13,17 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
|||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
|
||||
@Autowired
|
||||
private UserDetailsServiceImpl userDetailsService;
|
||||
private final UserDetailsServiceImpl userDetailsService;
|
||||
|
||||
public SecurityConfig(UserDetailsServiceImpl userDetailsService) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
|
@ -33,7 +33,7 @@ public class SecurityConfig {
|
|||
|
||||
@Bean
|
||||
public SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> securityConfigurerAdapter() {
|
||||
return new SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>() {
|
||||
return new SecurityConfigurerAdapter<>() {
|
||||
@Override
|
||||
public void configure(HttpSecurity http) {
|
||||
http.authenticationProvider(authenticationProvider());
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
package com.bitmutex.shortener;
|
||||
|
||||
import com.bitmutex.shortener.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/subscription")
|
||||
public class SubscriptionController {
|
||||
|
||||
@Autowired
|
||||
private SubscriptionService subscriptionService;
|
||||
private final SubscriptionService subscriptionService;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
private final UserService userService;
|
||||
|
||||
public SubscriptionController(SubscriptionService subscriptionService, UserService userService) {
|
||||
this.subscriptionService = subscriptionService;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@PostMapping("/change")
|
||||
public ResponseEntity<?> changeSubscription(@RequestParam String username, @RequestParam String newPlanName) {
|
||||
|
|
|
@ -3,7 +3,5 @@ package com.bitmutex.shortener;
|
|||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface SubscriptionRepository extends JpaRepository<Subscription, Long> {
|
||||
// You can add custom queries or methods related to subscriptions here
|
||||
// Custom query to find a subscription by user
|
||||
Subscription findByUser(UserEntity user);
|
||||
}
|
|
@ -3,8 +3,6 @@ package com.bitmutex.shortener;
|
|||
// UpdateUserRequest.java
|
||||
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class UpdateUserRequest {
|
||||
public String getUsername() {
|
||||
return username;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package com.bitmutex.shortener;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.Date;
|
||||
|
||||
public class UrlDetailsDTO {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package com.bitmutex.shortener;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
@ -12,37 +13,36 @@ import org.springframework.stereotype.Controller;
|
|||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.InetAddress;
|
||||
import java.net.URLEncoder;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Date;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
|
||||
@Slf4j
|
||||
@Controller
|
||||
public class UrlRedirectionController {
|
||||
|
||||
@Autowired
|
||||
private UrlShortenerRepository urlShortenerRepository;
|
||||
private final UrlShortenerRepository urlShortenerRepository;
|
||||
|
||||
@Autowired
|
||||
private AnalyticsRepository analyticsRepository;
|
||||
private final AnalyticsRepository analyticsRepository;
|
||||
|
||||
@Autowired
|
||||
private UrlShortenerService urlShortenerService;
|
||||
private final UrlShortenerService urlShortenerService;
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager entityManager;
|
||||
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
public UrlRedirectionController(UrlShortenerRepository urlShortenerRepository, AnalyticsRepository analyticsRepository, UrlShortenerService urlShortenerService, PasswordEncoder passwordEncoder) {
|
||||
this.urlShortenerRepository = urlShortenerRepository;
|
||||
this.analyticsRepository = analyticsRepository;
|
||||
this.urlShortenerService = urlShortenerService;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
@PostMapping("{shortUrl}")
|
||||
public String submitPassword(@PathVariable String shortUrl,
|
||||
|
|
|
@ -3,13 +3,11 @@ package com.bitmutex.shortener;
|
|||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.EncodeHintType;
|
||||
import com.google.zxing.WriterException;
|
||||
import com.google.zxing.client.j2se.MatrixToImageConfig;
|
||||
import com.google.zxing.client.j2se.MatrixToImageWriter;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.qrcode.QRCodeWriter;
|
||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
@ -19,8 +17,8 @@ import java.io.ByteArrayInputStream;
|
|||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
@Entity
|
||||
@Table(name = "url_shortener")
|
||||
|
@ -203,8 +201,7 @@ public class UrlShortener {
|
|||
return Base64.getEncoder().encodeToString(imageBytes);
|
||||
} catch (Exception e) {
|
||||
// Handle exceptions appropriately
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
throw new UrlShortenerException("Error generating B64QR for url",e);
|
||||
}
|
||||
}
|
||||
private void addLogoToQrCode(BufferedImage qrImage, String logoPath) throws IOException {
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
package com.bitmutex.shortener;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
||||
|
||||
@SpringBootApplication
|
||||
//@ComponentScan(basePackages = "com.bitmutex.shortener")
|
||||
public class UrlShortenerApplication {
|
||||
public class UrlShortenerApplication extends SpringBootServletInitializer {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(UrlShortenerApplication.class, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder){
|
||||
return builder.sources(UrlShortenerApplication.class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,27 +2,17 @@ package com.bitmutex.shortener;
|
|||
|
||||
import com.google.common.util.concurrent.RateLimiter;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
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.security.core.Authentication;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
@ -36,26 +26,28 @@ public class UrlShortenerController {
|
|||
@Value("${server.servlet.context-path:}") // Inject the context path from application properties
|
||||
private String contextPath;
|
||||
|
||||
@Autowired
|
||||
private UrlShortenerService service;
|
||||
private final UrlShortenerService service;
|
||||
|
||||
@Autowired
|
||||
private RateLimiter rateLimiter;
|
||||
private final RateLimiter rateLimiter;
|
||||
|
||||
@Autowired
|
||||
private UrlShortenerRepository urlShortenerRepository;
|
||||
private final UrlShortenerRepository urlShortenerRepository;
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
private final UserRepository userRepository;
|
||||
|
||||
@Value("${cooldown.duration}")
|
||||
private long cooldownSeconds;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
private final UserService userService;
|
||||
|
||||
private final ConcurrentHashMap<String, Long> cooldownMap = new ConcurrentHashMap<>();
|
||||
|
||||
public UrlShortenerController(UrlShortenerService service, RateLimiter rateLimiter, UrlShortenerRepository urlShortenerRepository, UserRepository userRepository, UserService userService) {
|
||||
this.service = service;
|
||||
this.rateLimiter = rateLimiter;
|
||||
this.urlShortenerRepository = urlShortenerRepository;
|
||||
this.userRepository = userRepository;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/shorten")
|
||||
|
@ -368,7 +360,7 @@ public class UrlShortenerController {
|
|||
|
||||
|
||||
protected String getClientId() {
|
||||
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
|
||||
HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
|
||||
String clientIp = request.getRemoteAddr();
|
||||
return "ip_" + clientIp; // Prefixing with "ip_" for clarity
|
||||
}
|
||||
|
|
|
@ -5,17 +5,18 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||
import jakarta.persistence.EntityNotFoundException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
|
@ -30,17 +31,13 @@ import java.util.regex.Pattern;
|
|||
@Slf4j
|
||||
public class UrlShortenerService {
|
||||
|
||||
@Autowired
|
||||
private UrlShortenerRepository repository;
|
||||
private final UrlShortenerRepository repository;
|
||||
|
||||
@Autowired
|
||||
private SubscriptionService subscriptionService;
|
||||
private final SubscriptionService subscriptionService;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
private final UserService userService;
|
||||
|
||||
@Autowired
|
||||
private HttpServletRequest request; // Inject the HttpServletRequest
|
||||
private final HttpServletRequest request; // Inject the HttpServletRequest
|
||||
|
||||
@Value("${server.port}") // Inject the server port from application properties
|
||||
private String serverPort;
|
||||
|
@ -51,8 +48,15 @@ public class UrlShortenerService {
|
|||
@Value("${server.base.url}")
|
||||
private String serverBaseUrl;
|
||||
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
public UrlShortenerService(UrlShortenerRepository repository, SubscriptionService subscriptionService, UserService userService, HttpServletRequest request, PasswordEncoder passwordEncoder) {
|
||||
this.repository = repository;
|
||||
this.subscriptionService = subscriptionService;
|
||||
this.userService = userService;
|
||||
this.request = request;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
public String shortenUrl(String jsonInput) {
|
||||
try {
|
||||
|
@ -79,9 +83,8 @@ public class UrlShortenerService {
|
|||
String linkType = "short";
|
||||
|
||||
|
||||
|
||||
// Get the user entity (you need to implement a method to fetch the current user entity)
|
||||
UserEntity userEntity = userService.findById(userId).get();
|
||||
UserEntity userEntity = userService.findById(userId).orElseThrow(() -> new UsernameNotFoundException("User with ID: "+userId+" not found"));
|
||||
|
||||
// Check if the user has exceeded the maximum short URL limit
|
||||
int maxShortUrlLimit = Integer.parseInt(subscriptionService.getCurrentSubscriptionDetails(userEntity).get("maxShortUrl").toString());
|
||||
|
@ -205,12 +208,12 @@ public class UrlShortenerService {
|
|||
}
|
||||
|
||||
// Regular expression for a simple URL format check
|
||||
String urlRegex = "^(https?://)?([a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})(/[a-zA-Z0-9-._?%&=]*)?$";
|
||||
String urlRegex = "^(https?://)?([a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})(/.*)?$";
|
||||
|
||||
try {
|
||||
// Check if the URL has a scheme, if not, assume it is HTTP
|
||||
if (!originalUrl.contains("://")) {
|
||||
originalUrl = "http://" + originalUrl;
|
||||
originalUrl = "https://" + originalUrl;
|
||||
}
|
||||
|
||||
// Basic URL format check using regex
|
||||
|
@ -289,7 +292,7 @@ public class UrlShortenerService {
|
|||
Long userId = getCurrentUserId();
|
||||
|
||||
// Get the user entity (you need to implement a method to fetch the current user entity)
|
||||
UserEntity userEntity = userService.findById(userId).get();
|
||||
UserEntity userEntity = userService.findById(userId).orElseThrow(() -> new UsernameNotFoundException("User with ID: "+userId+" not found"));
|
||||
|
||||
// Check if the user has exceeded the bio pages limit
|
||||
int currentBioPagesCount = repository.countBioPagesByUserId(userId);
|
||||
|
@ -330,13 +333,9 @@ public class UrlShortenerService {
|
|||
|
||||
public List<UrlShortener> getUrlsByUserId(Long userId) {
|
||||
try {
|
||||
// Assume that UrlDetails is an entity representing the details of a URL
|
||||
List<UrlShortener> urlDetailsList = repository.findByUserId(userId);
|
||||
|
||||
// You might need to convert entities to a DTO or customize the response based on your needs
|
||||
// For simplicity, let's assume UrlDetails is the entity itself
|
||||
|
||||
return urlDetailsList;
|
||||
// Assume that UrlDetails is an entity representing the details of a URL For simplicity, let's assume UrlDetails is the entity itself
|
||||
// We might need to convert entities to a DTO or customize the response based on your needs
|
||||
return repository.findByUserId(userId);
|
||||
} catch (Exception e) {
|
||||
// Handle exceptions or log them as needed
|
||||
throw new UrlShortenerException("Error retrieving URLs by user ID", e);
|
||||
|
@ -409,16 +408,30 @@ public class UrlShortenerService {
|
|||
}
|
||||
|
||||
|
||||
private boolean isValidHttpResponse(String originalUrl) {
|
||||
private static boolean isValidHttpResponse(String originalUrl) {
|
||||
try {
|
||||
URL url = new URL(originalUrl);
|
||||
final int CONNECT_TIMEOUT_MS = 1500; // 2 seconds
|
||||
final int READ_TIMEOUT_MS = 1500; // 2 seconds
|
||||
|
||||
URI uri = URI.create(originalUrl);
|
||||
URL url = uri.toURL();
|
||||
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setConnectTimeout(CONNECT_TIMEOUT_MS);
|
||||
connection.setReadTimeout(READ_TIMEOUT_MS);
|
||||
connection.setRequestProperty("User-Agent",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " +
|
||||
"(KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
return responseCode == HttpURLConnection.HTTP_OK;
|
||||
|
||||
System.out.println("URL VALIDATION RESPONSE CODE: " + responseCode + " for URL " + originalUrl);
|
||||
|
||||
// Consider both 200 (HTTP_OK) and 303 (HTTP_SEE_OTHER) as valid responses
|
||||
return responseCode == HttpURLConnection.HTTP_OK || responseCode == HttpURLConnection.HTTP_SEE_OTHER;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
throw new UrlShortenerException("Validation Error : Bad Response",e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +1,9 @@
|
|||
package com.bitmutex.shortener;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
|
@ -21,14 +15,14 @@ import java.net.URL;
|
|||
@RequestMapping("/api/user")
|
||||
public class UserController {
|
||||
private final UserService userService;
|
||||
@Autowired
|
||||
private OtpService otpService;
|
||||
private final OtpService otpService;
|
||||
|
||||
@Autowired
|
||||
private EmailService emailService;
|
||||
private final EmailService emailService;
|
||||
|
||||
public UserController(UserService userService) {
|
||||
public UserController(UserService userService, OtpService otpService, EmailService emailService) {
|
||||
this.userService = userService;
|
||||
this.otpService = otpService;
|
||||
this.emailService = emailService;
|
||||
}
|
||||
|
||||
@PostMapping("/register")
|
||||
|
@ -103,8 +97,7 @@ public class UserController {
|
|||
@GetMapping("/profile-picture")
|
||||
public ResponseEntity<byte[]> getUserProfilePicture(@RequestParam String username) {
|
||||
try {
|
||||
ResponseEntity<byte[]> image = userService.getProfilePictureByUsername(username);
|
||||
return image;
|
||||
return userService.getProfilePictureByUsername(username);
|
||||
} catch (UsernameNotFoundException e) {
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
|
||||
} catch (Exception e) {
|
||||
|
|
|
@ -1,26 +1,21 @@
|
|||
package com.bitmutex.shortener;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
|
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
|
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class UserDetailsServiceImpl implements UserDetailsService {
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
private final UserRepository userRepository;
|
||||
|
||||
public UserDetailsServiceImpl(UserRepository userRepository) {
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
|
|
|
@ -3,8 +3,6 @@ package com.bitmutex.shortener;
|
|||
import jakarta.persistence.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
@Entity
|
||||
@Table(name = "user_entity")
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
package com.bitmutex.shortener;
|
||||
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
|
||||
public class UserNotVerifiedException extends AuthenticationException {
|
||||
|
||||
public UserNotVerifiedException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
|
@ -3,7 +3,6 @@ package com.bitmutex.shortener;
|
|||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
|
@ -18,19 +17,19 @@ import java.util.Optional;
|
|||
@Service
|
||||
public class UserServiceImpl implements UserService {
|
||||
|
||||
@Autowired
|
||||
private SubscriptionService subscriptionService;
|
||||
private final SubscriptionService subscriptionService;
|
||||
|
||||
@Autowired
|
||||
private EmailService emailService;
|
||||
private final EmailService emailService;
|
||||
|
||||
private final UserRepository userRepository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
|
||||
public UserServiceImpl(UserRepository userRepository, PasswordEncoder passwordEncoder) {
|
||||
public UserServiceImpl(UserRepository userRepository, PasswordEncoder passwordEncoder, SubscriptionService subscriptionService, EmailService emailService) {
|
||||
this.userRepository = userRepository;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
this.subscriptionService = subscriptionService;
|
||||
this.emailService = emailService;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,27 +1,29 @@
|
|||
package com.bitmutex.shortener;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import javax.swing.text.html.Option;
|
||||
import java.util.Optional;
|
||||
|
||||
@Controller
|
||||
public class VerificationController {
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
private final UserService userService;
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
private final UserRepository userRepository;
|
||||
|
||||
@Autowired
|
||||
private OtpService otpService;
|
||||
private final OtpService otpService;
|
||||
|
||||
public VerificationController(UserService userService, UserRepository userRepository, OtpService otpService) {
|
||||
this.userService = userService;
|
||||
this.userRepository = userRepository;
|
||||
this.otpService = otpService;
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/verify-registration")
|
||||
|
@ -32,7 +34,7 @@ public class VerificationController {
|
|||
|
||||
|
||||
@PostMapping("/verify-registration")
|
||||
public ResponseEntity<String> verifyRegistration(@RequestParam String otp, @RequestParam String email, Model model) {
|
||||
public ResponseEntity<String> verifyRegistration(@RequestParam String otp, @RequestParam String email, Model ignoredModel) {
|
||||
|
||||
//String email = model.getAttribute("email").toString();
|
||||
|
||||
|
|
3
src/main/resources/META-INF/MANIFEST.MF
Normal file
3
src/main/resources/META-INF/MANIFEST.MF
Normal file
|
@ -0,0 +1,3 @@
|
|||
Manifest-Version: 1.0
|
||||
Main-Class: com.bitmutex.shortener.UrlShortenerApplication
|
||||
|
|
@ -6,7 +6,7 @@ database.ip=127.0.0.1
|
|||
# DataSource settings
|
||||
spring.datasource.url=jdbc:mysql://${database.ip}:${database.port}/${database.name}
|
||||
spring.datasource.username=root
|
||||
spring.datasource.password=
|
||||
spring.datasource.password=1234qwer
|
||||
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
|
||||
|
||||
|
@ -22,7 +22,7 @@ cooldown.duration=30
|
|||
requests.per.second=1
|
||||
|
||||
#Context Path(start with / and not end with /)
|
||||
server.servlet.context-path=
|
||||
server.servlet.context-path= /
|
||||
|
||||
#Server Port
|
||||
server.port=8080
|
||||
|
@ -37,7 +37,8 @@ spring.thymeleaf.cache=false
|
|||
|
||||
|
||||
#logging level
|
||||
logging.level.org.springframework.security: DEBUG
|
||||
logging.level.org.springframework.security= INFO
|
||||
logging.level.root=INFO
|
||||
|
||||
#SCHEMAGEN
|
||||
spring.jpa.properties.javax.persistence.schema-generation.scripts.action=create
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
</appender>
|
||||
|
||||
<!-- Root logger -->
|
||||
<root level="info">
|
||||
<root level="debug">
|
||||
<appender-ref ref="file"/>
|
||||
</root>
|
||||
</configuration>
|
|
@ -22,9 +22,36 @@
|
|||
|
||||
<!-- Company Logo with SVG -->
|
||||
<div class="flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="white" class="w-8 h-8 mr-2">
|
||||
<!-- Your SVG path here -->
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15h2v-6h-2zm0-8h2v-2h-2z" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="80" height="80" viewBox="0 0 120 120">
|
||||
|
||||
<!-- Background Parallelogram Shape -->
|
||||
<polygon points="0,0 0,0, 120,60,0,170" fill="#3498db" />
|
||||
|
||||
<!-- Wire -->
|
||||
<line x1="20" y1="50" x2="90" y2="50" stroke="#fff" stroke-width="5" />
|
||||
|
||||
<!-- Plug -->
|
||||
<rect x="50" y="40" width="20" height="20" fill="#e74c3c" />
|
||||
|
||||
<!-- T Letter -->
|
||||
<text x="2%" y="70%" font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="25" fill="#fff" font-weight="bold">
|
||||
<tspan font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="52" letter-spacing="2">T</tspan>
|
||||
</text>
|
||||
|
||||
<!-- U Letter -->
|
||||
<text x="20%" y="70%" font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="25" fill="#fff" font-weight="bold">
|
||||
<tspan font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="40" letter-spacing="2">U</tspan>
|
||||
</text>
|
||||
|
||||
<!-- S Letter -->
|
||||
<text x="40%" y="70%" font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="25" fill="#fff" font-weight="bold">
|
||||
<tspan font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="40" letter-spacing="2">S</tspan>
|
||||
</text>
|
||||
|
||||
<!-- C Letter -->
|
||||
<text x="58%" y="70%" font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="25" fill="#fff" font-weight="bold">
|
||||
<tspan font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="40" letter-spacing="2">C</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -37,9 +37,36 @@
|
|||
|
||||
<!-- Company Logo with SVG -->
|
||||
<div class="flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="white" class="w-8 h-8 mr-2">
|
||||
<!-- Your SVG path here -->
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15h2v-6h-2zm0-8h2v-2h-2z" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="80" height="80" viewBox="0 0 120 120">
|
||||
|
||||
<!-- Background Parallelogram Shape -->
|
||||
<polygon points="0,0 0,0, 120,60,0,170" fill="#3498db" />
|
||||
|
||||
<!-- Wire -->
|
||||
<line x1="20" y1="50" x2="90" y2="50" stroke="#fff" stroke-width="5" />
|
||||
|
||||
<!-- Plug -->
|
||||
<rect x="50" y="40" width="20" height="20" fill="#e74c3c" />
|
||||
|
||||
<!-- T Letter -->
|
||||
<text x="2%" y="70%" font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="25" fill="#fff" font-weight="bold">
|
||||
<tspan font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="52" letter-spacing="2">T</tspan>
|
||||
</text>
|
||||
|
||||
<!-- U Letter -->
|
||||
<text x="20%" y="70%" font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="25" fill="#fff" font-weight="bold">
|
||||
<tspan font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="40" letter-spacing="2">U</tspan>
|
||||
</text>
|
||||
|
||||
<!-- S Letter -->
|
||||
<text x="40%" y="70%" font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="25" fill="#fff" font-weight="bold">
|
||||
<tspan font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="40" letter-spacing="2">S</tspan>
|
||||
</text>
|
||||
|
||||
<!-- C Letter -->
|
||||
<text x="58%" y="70%" font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="25" fill="#fff" font-weight="bold">
|
||||
<tspan font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="40" letter-spacing="2">C</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -119,5 +119,4 @@
|
|||
});
|
||||
</script>
|
||||
</body>
|
||||
<div th:replace="layouts/footerLayout"></div>
|
||||
</html>
|
||||
|
|
|
@ -2,9 +2,41 @@
|
|||
<div class="mx-auto w-full max-w-screen-xl p-4 py-6 lg:py-8">
|
||||
<div class="md:flex md:justify-between">
|
||||
<div class="mb-6 md:mb-0">
|
||||
<a href="https://flowbite.com/" class="flex items-center">
|
||||
<img src="https://flowbite.com/docs/images/logo.svg" class="h-8 me-3" alt="FlowBite Logo" />
|
||||
<span class="self-center text-2xl font-semibold whitespace-nowrap dark:text-white">Flowbite</span>
|
||||
<a href="/" class="flex items-center">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="80" height="80" viewBox="0 0 120 120">
|
||||
|
||||
<!-- Background Parallelogram Shape -->
|
||||
<polygon points="0,0 0,0, 120,60,0,170" fill="#3498db" />
|
||||
|
||||
<!-- Wire -->
|
||||
<line x1="20" y1="50" x2="90" y2="50" stroke="#fff" stroke-width="5" />
|
||||
|
||||
<!-- Plug -->
|
||||
<rect x="50" y="40" width="20" height="20" fill="#e74c3c" />
|
||||
|
||||
<!-- T Letter -->
|
||||
<text x="2%" y="70%" font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="25" fill="#fff" font-weight="bold">
|
||||
<tspan font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="52" letter-spacing="2">T</tspan>
|
||||
</text>
|
||||
|
||||
<!-- U Letter -->
|
||||
<text x="20%" y="70%" font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="25" fill="#fff" font-weight="bold">
|
||||
<tspan font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="40" letter-spacing="2">U</tspan>
|
||||
</text>
|
||||
|
||||
<!-- S Letter -->
|
||||
<text x="40%" y="70%" font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="25" fill="#fff" font-weight="bold">
|
||||
<tspan font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="40" letter-spacing="2">S</tspan>
|
||||
</text>
|
||||
|
||||
<!-- C Letter -->
|
||||
<text x="58%" y="70%" font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="25" fill="#fff" font-weight="bold">
|
||||
<tspan font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="40" letter-spacing="2">C</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
|
||||
<h5>The Url Shortener Company</h5>
|
||||
</a>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-8 sm:gap-6 sm:grid-cols-3">
|
||||
|
@ -12,10 +44,10 @@
|
|||
<h2 class="mb-6 text-sm font-semibold text-gray-900 uppercase dark:text-white">Resources</h2>
|
||||
<ul class="text-gray-500 dark:text-gray-400 font-medium">
|
||||
<li class="mb-4">
|
||||
<a href="https://flowbite.com/" class="hover:underline">Flowbite</a>
|
||||
<a href="/docs-ui" class="hover:underline">API Docs</a>
|
||||
</li><br>
|
||||
<li>
|
||||
<a href="https://tailwindcss.com/" class="hover:underline">Tailwind CSS</a>
|
||||
<a href="/docs-ui" class="hover:underline">JAR/WAR Artifact</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -23,7 +55,7 @@
|
|||
<h2 class="mb-6 text-sm font-semibold text-gray-900 uppercase dark:text-white">Follow us</h2>
|
||||
<ul class="text-gray-500 dark:text-gray-400 font-medium">
|
||||
<li class="mb-4">
|
||||
<a href="https://github.com/themesberg/flowbite" class="hover:underline ">Github</a>
|
||||
<a href="https://github.com/aamitn/URLShortener" class="hover:underline ">Github</a>
|
||||
</li><br>
|
||||
<li>
|
||||
<a href="https://discord.gg/4eeurUVvTy" class="hover:underline">Discord</a>
|
||||
|
@ -45,7 +77,7 @@
|
|||
</div>
|
||||
<hr class="my-6 border-gray-200 sm:mx-auto dark:border-gray-700 lg:my-8" />
|
||||
<div class="sm:flex sm:items-center sm:justify-between">
|
||||
<span class="text-sm text-gray-500 sm:text-center dark:text-gray-400">© 2023 <a href="https://flowbite.com/" class="hover:underline">Flowbite™</a>. All Rights Reserved.
|
||||
<span class="text-sm text-gray-500 sm:text-center dark:text-gray-400">© 2024 <a href="https://bitmutex.com/" class="hover:underline">Bitmutex Technologies</a>. All Rights Reserved.
|
||||
</span>
|
||||
<div class="flex mt-4 sm:justify-center sm:mt-0">
|
||||
<a href="#" class="text-gray-500 hover:text-gray-900 dark:hover:text-white">
|
||||
|
|
|
@ -2,11 +2,41 @@
|
|||
<div id="navbar" class="flex flex-wrap bg-gray-800 text-white py-4 px-8 mx-auto">
|
||||
|
||||
<div class="flex items-center mb-4 w-full md:w-auto md:mr-auto">
|
||||
<!-- Company Logo with SVG -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="white" class="w-8 h-8 mr-2">
|
||||
<!-- Your SVG path here -->
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15h2v-6h-2zm0-8h2v-2h-2z" />
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="80" height="80" viewBox="0 0 120 120">
|
||||
|
||||
<!-- Background Parallelogram Shape -->
|
||||
<polygon points="0,0 0,0, 120,60,0,170" fill="#3498db" />
|
||||
|
||||
<!-- Wire -->
|
||||
<line x1="20" y1="50" x2="90" y2="50" stroke="#fff" stroke-width="5" />
|
||||
|
||||
<!-- Plug -->
|
||||
<rect x="50" y="40" width="20" height="20" fill="#e74c3c" />
|
||||
|
||||
<!-- T Letter -->
|
||||
<text x="2%" y="70%" font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="25" fill="#fff" font-weight="bold">
|
||||
<tspan font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="52" letter-spacing="2">T</tspan>
|
||||
</text>
|
||||
|
||||
<!-- U Letter -->
|
||||
<text x="20%" y="70%" font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="25" fill="#fff" font-weight="bold">
|
||||
<tspan font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="40" letter-spacing="2">U</tspan>
|
||||
</text>
|
||||
|
||||
<!-- S Letter -->
|
||||
<text x="40%" y="70%" font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="25" fill="#fff" font-weight="bold">
|
||||
<tspan font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="40" letter-spacing="2">S</tspan>
|
||||
</text>
|
||||
|
||||
<!-- C Letter -->
|
||||
<text x="58%" y="70%" font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="25" fill="#fff" font-weight="bold">
|
||||
<tspan font-family="'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" font-size="40" letter-spacing="2">C</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<ul class="flex flex-wrap justify-start space-x-4 items-center w-full md:w-auto">
|
||||
|
|
|
@ -18,18 +18,20 @@
|
|||
<div class="mb-8">
|
||||
<ul class="flex border-b">
|
||||
<li class="-mb-px mr-1">
|
||||
<a href="javascript:void(0)" onclick="openTab(event, 'createBio')" class="bg-white inline-block py-2 px-4 font-semibold">Create Bio</a>
|
||||
<a href="javascript:void(0)" onclick="openTab(event, 'createBio')" class="bg-white inline-block py-2 px-4 font-semibold"><i class="fa fa-user"></i> Create Bio</a>
|
||||
</li>
|
||||
<li class="-mb-px mr-1">
|
||||
<a href="javascript:void(0)" onclick="openTab(event, 'shortenURL')" class="bg-white inline-block py-2 px-4 font-semibold">Shorten URL</a>
|
||||
<a href="javascript:void(0)" onclick="openTab(event, 'shortenURL')" class="bg-white inline-block py-2 px-4 font-semibold"><i class="fa fa-link"></i> Shorten URL</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div id="createBio" class="tab-content">
|
||||
<div class="bg-white p-6 rounded-lg shadow-md mt-4">
|
||||
<h2 class="text-xl font-bold mb-4">Create Bio Form</h2>
|
||||
<h2 class="text-xl font-bold mb-4">Create Your own customized bio page</h2>
|
||||
<!-- Bio creation form goes here -->
|
||||
<form>
|
||||
<form onsubmit="window.location.href='/login'; return false;">
|
||||
<textarea id="htmlData" rows="2" class="block mx-4 p-2.5 w-3/4 text-sm text-gray-900 bg-white rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-800 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="Your Bio Page Content..." ></textarea>
|
||||
<p></p>
|
||||
<!-- Bio form fields go here -->
|
||||
<button type="submit" class="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-full mt-4">Create Bio</button>
|
||||
</form>
|
||||
|
@ -38,9 +40,15 @@
|
|||
|
||||
<div id="shortenURL" class="hidden tab-content">
|
||||
<div class="bg-white p-6 rounded-lg shadow-md mt-4">
|
||||
<h2 class="text-xl font-bold mb-4">Shorten URL Form</h2>
|
||||
<h2 class="text-xl font-bold mb-4">Shorten your long url</h2>
|
||||
<!-- URL shortening form goes here -->
|
||||
<form>
|
||||
<form onsubmit="window.location.href='/login'; return false;">
|
||||
<input type="text" id="originalUrl" placeholder="Enter the original URL"
|
||||
class="px-4 py-2 border border-gray-300 rounded focus:outline-none">
|
||||
<p></p>
|
||||
<input type="text" id="backHalf" placeholder="Custom Backhalf"
|
||||
class="px-4 py-2 border border-gray-300 rounded focus:outline-none">
|
||||
<p></p>
|
||||
<!-- URL form fields go here -->
|
||||
<button type="submit" class="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-full mt-4">Shorten URL</button>
|
||||
</form>
|
||||
|
@ -55,18 +63,28 @@
|
|||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
<!-- Feature Card 1 -->
|
||||
<div class="bg-white p-6 rounded-lg shadow-md">
|
||||
<h3 class="text-xl font-bold mb-2">Feature 1</h3>
|
||||
<p class="text-gray-600">Description of feature 1.</p>
|
||||
<h3 class="text-xl font-bold mb-2"><i class="fas fa-link"></i> Custom Short URLs</h3>
|
||||
<p class="text-gray-600">Generate custom short URLs that reflect your brand or content.</p>
|
||||
</div>
|
||||
<!-- Feature Card 2 -->
|
||||
<div class="bg-white p-6 rounded-lg shadow-md">
|
||||
<h3 class="text-xl font-bold mb-2">Feature 2</h3>
|
||||
<p class="text-gray-600">Description of feature 2.</p>
|
||||
<h3 class="text-xl font-bold mb-2"><i class="fas fa-lock"></i> Password Protection</h3>
|
||||
<p class="text-gray-600">Secure your short URLs with password protection, ensuring access only to authorized users.</p>
|
||||
</div>
|
||||
<!-- Feature Card 3 -->
|
||||
<div class="bg-white p-6 rounded-lg shadow-md">
|
||||
<h3 class="text-xl font-bold mb-2">Feature 3</h3>
|
||||
<p class="text-gray-600">Description of feature 3.</p>
|
||||
<h3 class="text-xl font-bold mb-2"><i class="fas fa-toggle-on"></i> Link State Management</h3>
|
||||
<p class="text-gray-600">Control the state of your short URLs, enabling or disabling them based on your preferences.</p>
|
||||
</div>
|
||||
<!-- Feature Card 4 -->
|
||||
<div class="bg-white p-6 rounded-lg shadow-md">
|
||||
<h3 class="text-xl font-bold mb-2"><i class="fas fa-user-circle"></i> Bio Pages</h3>
|
||||
<p class="text-gray-600">Create personalized bio pages to share additional information about yourself or your business.</p>
|
||||
</div>
|
||||
<!-- Feature Card 5 (Add more features as needed) -->
|
||||
<div class="bg-white p-6 rounded-lg shadow-md">
|
||||
<h3 class="text-xl font-bold mb-2"><i class="fas fa-chart-bar"></i> Detailed Analytics</h3>
|
||||
<p class="text-gray-600">Access detailed analytics for each shortened URL, including click counts and user data.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -75,92 +93,109 @@
|
|||
<div class="mb-8">
|
||||
<h2 class="text-3xl font-bold mb-4 text-center">Pricing</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
<!-- Pricing Card 1 -->
|
||||
<div class="bg-white p-6 rounded-lg shadow-md">
|
||||
<h3 class="text-xl font-bold mb-2">Basic Plan</h3>
|
||||
<p class="text-gray-600">$19.99/month</p>
|
||||
<!-- Free Plan -->
|
||||
<div class="border-solid border-2 border-sky-500 bg-white p-6 rounded-lg shadow-md text-center">
|
||||
<h3 class="text-2xl font-bold mb-4">Free Plan</h3>
|
||||
<p class="text-gray-600">10 Short URLs</p>
|
||||
<p class="text-gray-600">5 Bio Pages</p>
|
||||
<p class="text-gray-600">Price: $0/month</p>
|
||||
</div>
|
||||
<!-- Pricing Card 2 -->
|
||||
<div class="bg-white p-6 rounded-lg shadow-md">
|
||||
<h3 class="text-xl font-bold mb-2">Pro Plan</h3>
|
||||
<p class="text-gray-600">$39.99/month</p>
|
||||
|
||||
<!-- Basic Plan -->
|
||||
<div class="border-solid border-2 border-sky-500 bg-white p-6 rounded-lg shadow-md text-center">
|
||||
<h3 class="text-2xl font-bold mb-4">Basic Plan</h3>
|
||||
<p class="text-gray-600">50 Short URLs</p>
|
||||
<p class="text-gray-600">20 Bio Pages</p>
|
||||
<p class="text-gray-600">Price: INR 89/month</p>
|
||||
</div>
|
||||
<!-- Pricing Card 3 -->
|
||||
<div class="bg-white p-6 rounded-lg shadow-md">
|
||||
<h3 class="text-xl font-bold mb-2">Enterprise Plan</h3>
|
||||
<p class="text-gray-600">Contact us for a quote</p>
|
||||
|
||||
<!-- Pro Plan -->
|
||||
<div class="border-solid border-2 border-sky-500 bg-white p-6 rounded-lg shadow-md text-center">
|
||||
<h3 class="text-2xl font-bold mb-4">Pro Plan</h3>
|
||||
<p class="text-gray-600">100 Short URLs</p>
|
||||
<p class="text-gray-600">50 Bio Pages</p>
|
||||
<p class="text-gray-600">Price: INR 129/month</p>
|
||||
</div>
|
||||
|
||||
<!-- Enterprise Plan -->
|
||||
<div class="border-solid border-2 border-sky-500 bg-white p-6 rounded-lg shadow-md text-center">
|
||||
<h3 class="text-2xl font-bold mb-4">Enterprise Plan</h3>
|
||||
<p class="text-gray-600">Unlimited Short URLs</p>
|
||||
<p class="text-gray-600">Unlimited Bio Pages</p>
|
||||
<p class="text-gray-600">Contact us for pricing</p>
|
||||
<a href="/contact" class="text-blue-500 font-semibold">Contact Us</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Flowbite Accordion Section -->
|
||||
|
||||
<!-- URL Shortener FAQ Section -->
|
||||
<div class="bg-white rounded-lg overflow-hidden shadow-md">
|
||||
|
||||
<div id="accordion-open" data-accordion="open">
|
||||
<!-- FAQ Item 1 -->
|
||||
<h2 id="accordion-open-heading-1">
|
||||
<button type="button" class="flex items-center justify-between w-full p-5 font-medium rtl:text-right text-gray-500 border border-b-0 border-gray-200 rounded-t-xl focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-800 dark:border-gray-700 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 gap-3" data-accordion-target="#accordion-open-body-1" aria-expanded="true" aria-controls="accordion-open-body-1">
|
||||
<div id="accordion-open" data-accordion="open">
|
||||
<!-- FAQ Item 1 -->
|
||||
<h2 id="accordion-open-heading-1">
|
||||
<button type="button" class="flex items-center justify-between w-full p-5 font-medium rtl:text-right text-gray-500 border border-b-0 border-gray-200 rounded-t-xl focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-800 dark:border-gray-700 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 gap-3" data-accordion-target="#accordion-open-body-1" aria-expanded="true" aria-controls="accordion-open-body-1">
|
||||
<span class="flex items-center">
|
||||
<svg class="w-5 h-5 me-2 shrink-0" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-3a1 1 0 00-.867.5 1 1 0 11-1.731-1A3 3 0 0113 8a3.001 3.001 0 01-2 2.83V11a1 1 0 11-2 0v-1a1 1 0 011-1 1 1 0 100-2zm0 8a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd"></path>
|
||||
</svg> What is Flowbite?
|
||||
</svg> What is our URL Shortener service?
|
||||
</span>
|
||||
<svg data-accordion-icon class="w-3 h-3 rotate-180 shrink-0" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5 5 1 1 5"/>
|
||||
</svg>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="accordion-open-body-1" class="hidden" aria-labelledby="accordion-open-heading-1">
|
||||
<div class="p-5 border border-b-0 border-gray-200 dark:border-gray-700 dark:bg-gray-900">
|
||||
<p class="mb-2 text-gray-500 dark:text-gray-400">Flowbite is an open-source library of interactive components built on top of Tailwind CSS, including buttons, dropdowns, modals, navbars, and more.</p>
|
||||
<p class="text-gray-500 dark:text-gray-400">Check out this guide to learn how to <a href="/docs/getting-started/introduction/" class="text-blue-600 dark:text-blue-500 hover:underline">get started</a> and start developing websites even faster with components on top of Tailwind CSS.</p>
|
||||
<svg data-accordion-icon class="w-3 h-3 rotate-180 shrink-0" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5 5 1 1 5"/>
|
||||
</svg>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="accordion-open-body-1" class="hidden" aria-labelledby="accordion-open-heading-1">
|
||||
<div class="p-5 border border-b-0 border-gray-200 dark:border-gray-700 dark:bg-gray-900">
|
||||
<p class="mb-2 text-gray-500 dark:text-gray-400">Our URL Shortener service is a powerful tool that allows you to create short and shareable links. Whether you're looking to track clicks, manage branded links, or enhance user experience, our service has you covered.</p>
|
||||
<p class="text-gray-500 dark:text-gray-400">Check out our <a href="/docs-ui" class="text-blue-600 dark:text-blue-500 hover:underline">documentation</a> to learn how to get started with our URL Shortener service.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- FAQ Item 2 -->
|
||||
<h2 id="accordion-open-heading-2">
|
||||
<button type="button" class="flex items-center justify-between w-full p-5 font-medium rtl:text-right text-gray-500 border border-b-0 border-gray-200 focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-800 dark:border-gray-700 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 gap-3" data-accordion-target="#accordion-open-body-2" aria-expanded="false" aria-controls="accordion-open-body-2">
|
||||
<!-- FAQ Item 2 -->
|
||||
<h2 id="accordion-open-heading-2">
|
||||
<button type="button" class="flex items-center justify-between w-full p-5 font-medium rtl:text-right text-gray-500 border border-b-0 border-gray-200 focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-800 dark:border-gray-700 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 gap-3" data-accordion-target="#accordion-open-body-2" aria-expanded="false" aria-controls="accordion-open-body-2">
|
||||
<span class="flex items-center">
|
||||
<svg class="w-5 h-5 me-2 shrink-0" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-3a1 1 0 00-.867.5 1 1 0 11-1.731-1A3 3 0 0113 8a3.001 3.001 0 01-2 2.83V11a1 1 0 11-2 0v-1a1 1 0 011-1 1 1 0 100-2zm0 8a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd"></path>
|
||||
</svg> Is there a Figma file available?
|
||||
</svg> Can I customize my short URLs?
|
||||
</span>
|
||||
<svg data-accordion-icon class="w-3 h-3 rotate-180 shrink-0" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5 5 1 1 5"/>
|
||||
</svg>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="accordion-open-body-2" class="hidden" aria-labelledby="accordion-open-heading-2">
|
||||
<div class="p-5 border border-b-0 border-gray-200 dark:border-gray-700">
|
||||
<p class="mb-2 text-gray-500 dark:text-gray-400">Flowbite is first conceptualized and designed using the Figma software so everything you see in the library has a design equivalent in our Figma file.</p>
|
||||
<p class="text-gray-500 dark:text-gray-400">Check out the <a href="https://flowbite.com/figma/" class="text-blue-600 dark:text-blue-500 hover:underline">Figma design system</a> based on the utility classes from Tailwind CSS and components from Flowbite.</p>
|
||||
<svg data-accordion-icon class="w-3 h-3 rotate-180 shrink-0" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5 5 1 1 5"/>
|
||||
</svg>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="accordion-open-body-2" class="hidden" aria-labelledby="accordion-open-heading-2">
|
||||
<div class="p-5 border border-b-0 border-gray-200 dark:border-gray-700">
|
||||
<p class="mb-2 text-gray-500 dark:text-gray-400">Absolutely! With our URL Shortener service, you have the flexibility to customize your short URLs to reflect your brand or campaign. Add your own keywords or choose a custom alias to make your links more recognizable and meaningful.</p>
|
||||
<p class="text-gray-500 dark:text-gray-400">Learn how to create custom short URLs in our <a href="https://your-url-shortener-docs.com/customization" class="text-blue-600 dark:text-blue-500 hover:underline">customization guide</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- FAQ Item 3 -->
|
||||
<h2 id="accordion-open-heading-3">
|
||||
<button type="button" class="flex items-center justify-between w-full p-5 font-medium rtl:text-right text-gray-500 border border-gray-200 focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-800 dark:border-gray-700 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 gap-3" data-accordion-target="#accordion-open-body-3" aria-expanded="false" aria-controls="accordion-open-body-3">
|
||||
<!-- FAQ Item 3 -->
|
||||
<h2 id="accordion-open-heading-3">
|
||||
<button type="button" class="flex items-center justify-between w-full p-5 font-medium rtl:text-right text-gray-500 border border-gray-200 focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-800 dark:border-gray-700 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 gap-3" data-accordion-target="#accordion-open-body-3" aria-expanded="false" aria-controls="accordion-open-body-3">
|
||||
<span class="flex items-center">
|
||||
<svg class="w-5 h-5 me-2 shrink-0" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-3a1 1 0 00-.867.5 1 1 0 11-1.731-1A3 3 0 0113 8a3.001 3.001 0 01-2 2.83V11a1 1 0 11-2 0v-1a1 1 0 011-1 1 1 0 100-2zm0 8a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd"></path>
|
||||
</svg> What are the differences between Flowbite and Tailwind UI?
|
||||
</svg> How can I track the performance of my short URLs?
|
||||
</span>
|
||||
<svg data-accordion-icon class="w-3 h-3 rotate-180 shrink-0" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5 5 1 1 5"/>
|
||||
</svg>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="accordion-open-body-3" class="hidden" aria-labelledby="accordion-open-heading-3">
|
||||
<div class="p-5 border border-t-0 border-gray-200 dark:border-gray-700">
|
||||
<p class="mb-2 text-gray-500 dark:text-gray-400">The main difference is that the core components from Flowbite are open source under the MIT license, whereas Tailwind UI is a paid product. Another difference is that Flowbite relies on smaller and standalone components, whereas Tailwind UI offers sections of pages.</p>
|
||||
<p class="mb-2 text-gray-500 dark:text-gray-400">However, we actually recommend using both Flowbite, Flowbite Pro, and even Tailwind UI as there is no technical reason stopping you from using the best of two worlds.</p>
|
||||
<p class="mb-2 text-gray-500 dark:text-gray-400">Learn more about these technologies:</p>
|
||||
<ul class="ps-5 text-gray-500 list-disc dark:text-gray-400">
|
||||
<li><a href="https://flowbite.com/pro/" class="text-blue-600 dark:text-blue-500 hover:underline">Flowbite Pro</a></li>
|
||||
<li><a href="https://tailwindui.com/" rel="nofollow" class="text-blue-600 dark:text-blue-500 hover:underline">Tailwind UI</a></li>
|
||||
</ul>
|
||||
<svg data-accordion-icon class="w-3 h-3 rotate-180 shrink-0" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5 5 1 1 5"/>
|
||||
</svg>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="accordion-open-body-3" class="hidden" aria-labelledby="accordion-open-heading-3">
|
||||
<div class="p-5 border border-t-0 border-gray-200 dark:border-gray-700">
|
||||
<p class="mb-2 text-gray-500 dark:text-gray-400">Our URL Shortener service provides comprehensive analytics to help you track the performance of your short URLs. Monitor the number of clicks, geographic location of users, and referral sources in real-time.</p>
|
||||
<p class="mb-2 text-gray-500 dark:text-gray-400">Explore the detailed analytics dashboard in our <a href="https://your-url-shortener-dashboard.com" class="text-blue-600 dark:text-blue-500 hover:underline">dashboard guide</a> to gain valuable insights into your link engagement.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- Call to Action Section -->
|
||||
<div class="bg-gray-800 text-white p-8 rounded-lg shadow-md text-center">
|
||||
<h2 class="text-2xl font-bold mb-4">Ready to get started?</h2>
|
||||
|
|
|
@ -102,53 +102,61 @@
|
|||
<!-- Add more sections for other Actuator endpoints as needed -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
// Fetch health data using JS fetch
|
||||
fetch('/actuator/health')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
// Update the content of the healthDataContainer with the fetched data
|
||||
document.getElementById('healthDataContainer').innerText = JSON.stringify(data, null, 2);
|
||||
//Function to Fetch health data using JS fetch
|
||||
function fetchHealthData() {
|
||||
fetch('/actuator/health')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
// Update the content of the healthDataContainer with the fetched data
|
||||
document.getElementById('healthDataContainer').innerText = JSON.stringify(data, null, 2);
|
||||
|
||||
// Parse JSON data
|
||||
var json = JSON.parse(JSON.stringify(data));
|
||||
// Parse JSON data
|
||||
var json = JSON.parse(JSON.stringify(data));
|
||||
|
||||
// Update Component Status
|
||||
updateComponentStatus('app', json.components && json.components.custom);
|
||||
updateComponentStatus('db', json.components && json.components.db);
|
||||
updateComponentStatus('diskSpace', json.components && json.components.diskSpace);
|
||||
updateComponentStatus('mail', json.components && json.components.mail);
|
||||
updateComponentStatus('ping', json.components && json.components.ping);
|
||||
// Update Component Status
|
||||
updateComponentStatus('app', json.components && json.components.custom);
|
||||
updateComponentStatus('db', json.components && json.components.db);
|
||||
updateComponentStatus('diskSpace', json.components && json.components.diskSpace);
|
||||
updateComponentStatus('mail', json.components && json.components.mail);
|
||||
updateComponentStatus('ping', json.components && json.components.ping);
|
||||
|
||||
// Extract disk space information
|
||||
const diskSpace = json.components && json.components.diskSpace;
|
||||
// Extract disk space information
|
||||
const diskSpace = json.components && json.components.diskSpace;
|
||||
|
||||
// Prepare data for pie chart
|
||||
if (diskSpace && diskSpace.status === 'UP' && diskSpace.details) {
|
||||
const free = diskSpace.details.free;
|
||||
const total = diskSpace.details.total;
|
||||
// Prepare data for pie chart
|
||||
if (diskSpace && diskSpace.status === 'UP' && diskSpace.details) {
|
||||
const free = diskSpace.details.free;
|
||||
const total = diskSpace.details.total;
|
||||
|
||||
console.log(free/100000);
|
||||
console.log(total/100000);
|
||||
console.log(free / 100000);
|
||||
console.log(total / 100000);
|
||||
|
||||
// Create a disk space pie chart using Chart.js
|
||||
const ctx = document.getElementById('diskSpaceChart').getContext('2d');
|
||||
const diskSpaceChart = new Chart(ctx, {
|
||||
type: 'pie',
|
||||
data: {
|
||||
labels: ['Total Space', 'Free Space'],
|
||||
datasets: [{
|
||||
data: [total, free],
|
||||
backgroundColor: ['rgba(255, 99, 132, 0.7)', 'rgba(75, 192, 192, 0.7)'],
|
||||
borderWidth: 2
|
||||
}]
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.error('Error: Disk space data not available or status is not UP.');
|
||||
}
|
||||
})
|
||||
.catch(error => console.error('Error fetching health data:', error));
|
||||
// Create a disk space pie chart using Chart.js
|
||||
const ctx = document.getElementById('diskSpaceChart').getContext('2d');
|
||||
const diskSpaceChart = new Chart(ctx, {
|
||||
type: 'pie',
|
||||
data: {
|
||||
labels: ['Total Space', 'Free Space'],
|
||||
datasets: [{
|
||||
data: [total, free],
|
||||
backgroundColor: ['rgba(255, 99, 132, 0.7)', 'rgba(75, 192, 192, 0.7)'],
|
||||
borderWidth: 2
|
||||
}]
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.error('Error: Disk space data not available or status is not UP.');
|
||||
}
|
||||
})
|
||||
.catch(error => console.error('Error fetching health data:', error));
|
||||
}
|
||||
|
||||
// Fetch data initially when the page loads
|
||||
fetchHealthData();
|
||||
|
||||
// Fetch data every 2 seconds (adjust the interval as needed)
|
||||
const fetchInterval = 2000; // 5 seconds
|
||||
setInterval(fetchHealthData, fetchInterval);
|
||||
|
||||
// Function to update component status
|
||||
function updateComponentStatus(componentId, component) {
|
||||
|
@ -172,7 +180,7 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
</div>
|
||||
|
||||
<div th:replace="layouts/footerLayout"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
9
src/main/webapp/WEB-INF/web.xml
Normal file
9
src/main/webapp/WEB-INF/web.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
|
||||
version="4.0">
|
||||
|
||||
<display-name>TUSC - The URL Shortener Company</display-name>
|
||||
|
||||
</web-app>
|
Loading…
Reference in a new issue