Browse Source

Initial commit on 26 December,2025

master
example 10 months ago
commit
ddfb35ddc3
  1. 2
      .gitattributes
  2. 33
      .gitignore
  3. 3
      .mvn/wrapper/maven-wrapper.properties
  4. 295
      mvnw
  5. 189
      mvnw.cmd
  6. 140
      pom.xml
  7. 17
      src/main/java/com/gcc_it_cell_services/springboot/GccItCellServicesApplication.java
  8. 41
      src/main/java/com/gcc_it_cell_services/springboot/configuration/AuditorAwareConfiguration.java
  9. 42
      src/main/java/com/gcc_it_cell_services/springboot/configuration/RateLimitConfiguration.java
  10. 136
      src/main/java/com/gcc_it_cell_services/springboot/configuration/SecurityConfiguration.java
  11. 37
      src/main/java/com/gcc_it_cell_services/springboot/configuration/WebConfiguration.java
  12. 70
      src/main/java/com/gcc_it_cell_services/springboot/controller/HomeController.java
  13. 61
      src/main/java/com/gcc_it_cell_services/springboot/controller/UrlDataController.java
  14. 102
      src/main/java/com/gcc_it_cell_services/springboot/interceptor/RateLimitInterceptor.java
  15. 38
      src/main/java/com/gcc_it_cell_services/springboot/mapper/OtpLogMapper.java
  16. 19
      src/main/java/com/gcc_it_cell_services/springboot/mapper/StatusMapper.java
  17. 29
      src/main/java/com/gcc_it_cell_services/springboot/mapper/UrlCategoryMapper.java
  18. 48
      src/main/java/com/gcc_it_cell_services/springboot/mapper/UrlDataMapper.java
  19. 70
      src/main/java/com/gcc_it_cell_services/springboot/model/AppUser.java
  20. 102
      src/main/java/com/gcc_it_cell_services/springboot/model/OtpLog.java
  21. 35
      src/main/java/com/gcc_it_cell_services/springboot/model/Status.java
  22. 106
      src/main/java/com/gcc_it_cell_services/springboot/model/UrlCategory.java
  23. 136
      src/main/java/com/gcc_it_cell_services/springboot/model/UrlData.java
  24. 66
      src/main/java/com/gcc_it_cell_services/springboot/model/UserRole.java
  25. 25
      src/main/java/com/gcc_it_cell_services/springboot/repository/AppUserRepository.java
  26. 15
      src/main/java/com/gcc_it_cell_services/springboot/repository/OtpLogRepository.java
  27. 11
      src/main/java/com/gcc_it_cell_services/springboot/repository/StatusRepository.java
  28. 18
      src/main/java/com/gcc_it_cell_services/springboot/repository/UrlCategoryRepository.java
  29. 28
      src/main/java/com/gcc_it_cell_services/springboot/repository/UrlDataRepository.java
  30. 22
      src/main/java/com/gcc_it_cell_services/springboot/request_dto/AddUrlDataRequestDTO.java
  31. 17
      src/main/java/com/gcc_it_cell_services/springboot/request_dto/AppUserRequestDTO.java
  32. 18
      src/main/java/com/gcc_it_cell_services/springboot/request_dto/GetOtpRequestDTO.java
  33. 22
      src/main/java/com/gcc_it_cell_services/springboot/request_dto/OtpLogRequestDTO.java
  34. 22
      src/main/java/com/gcc_it_cell_services/springboot/request_dto/UpdateUrlDataRequestDTO.java
  35. 18
      src/main/java/com/gcc_it_cell_services/springboot/request_dto/UrlCategoryRequestDTO.java
  36. 26
      src/main/java/com/gcc_it_cell_services/springboot/request_dto/UrlDataRequestDTO.java
  37. 19
      src/main/java/com/gcc_it_cell_services/springboot/request_dto/VerifyOtpRequestDTO.java
  38. 23
      src/main/java/com/gcc_it_cell_services/springboot/response_dto/OtpLogResponseDTO.java
  39. 23
      src/main/java/com/gcc_it_cell_services/springboot/response_dto/ServiceResponseDTO.java
  40. 23
      src/main/java/com/gcc_it_cell_services/springboot/response_dto/StatusResponseDTO.java
  41. 19
      src/main/java/com/gcc_it_cell_services/springboot/response_dto/UrlCategoryResponseDTO.java
  42. 37
      src/main/java/com/gcc_it_cell_services/springboot/response_dto/UrlDataResponseDTO.java
  43. 192
      src/main/java/com/gcc_it_cell_services/springboot/rest_controller/HomeRestController.java
  44. 43
      src/main/java/com/gcc_it_cell_services/springboot/rest_controller/UrlDataRestController.java
  45. 279
      src/main/java/com/gcc_it_cell_services/springboot/service/HomeService.java
  46. 174
      src/main/java/com/gcc_it_cell_services/springboot/service/UrlDataCacheService.java
  47. 280
      src/main/java/com/gcc_it_cell_services/springboot/service/UrlDataService.java
  48. 28
      src/main/java/com/gcc_it_cell_services/springboot/util/MobileNumberTokenStore.java
  49. 21
      src/main/java/com/gcc_it_cell_services/springboot/util/UrlCacheScheduler.java
  50. 22
      src/main/java/com/gcc_it_cell_services/springboot/util/UrlDataCacheInitializer.java
  51. 9
      src/main/resources/META-INF/additional-spring-configuration-metadata.json
  52. 33
      src/main/resources/application-local.properties
  53. 29
      src/main/resources/application-prod.properties
  54. 2
      src/main/resources/application.properties
  55. 11794
      src/main/resources/static/assets/bootstrap-5.3.2/css/bootstrap.min.css
  56. 2212
      src/main/resources/static/assets/bootstrap-5.3.2/js/bootstrap.min.js
  57. 193
      src/main/resources/static/assets/css/body-form-styles.css
  58. 114
      src/main/resources/static/assets/css/datatable-styles.css
  59. 77
      src/main/resources/static/assets/css/header-styles.css
  60. 295
      src/main/resources/static/assets/css/home-styles.css
  61. 272
      src/main/resources/static/assets/css/login-form-styles.css
  62. 532
      src/main/resources/static/assets/datatables-1.13.6/css/dataTables.bootstrap5.min.css
  63. 80
      src/main/resources/static/assets/datatables-1.13.6/js/dataTables.bootstrap5.min.js
  64. 2990
      src/main/resources/static/assets/datatables-1.13.6/js/jquery.dataTables.min.js
  65. 248
      src/main/resources/static/assets/datatables-buttons-2.4.1/css/buttons.bootstrap5.min.css
  66. 70
      src/main/resources/static/assets/datatables-buttons-2.4.1/js/buttons.bootstrap5.min.js
  67. 538
      src/main/resources/static/assets/datatables-buttons-2.4.1/js/buttons.html5.min.js
  68. 79
      src/main/resources/static/assets/datatables-buttons-2.4.1/js/buttons.print.min.js
  69. 764
      src/main/resources/static/assets/datatables-buttons-2.4.1/js/dataTables.buttons.min.js
  70. 6995
      src/main/resources/static/assets/font-awesome-6.5.1/css/all.min.css
  71. BIN
      src/main/resources/static/assets/font-awesome-6.5.1/webfonts/fa-brands-400.ttf
  72. BIN
      src/main/resources/static/assets/font-awesome-6.5.1/webfonts/fa-brands-400.woff2
  73. BIN
      src/main/resources/static/assets/font-awesome-6.5.1/webfonts/fa-regular-400.ttf
  74. BIN
      src/main/resources/static/assets/font-awesome-6.5.1/webfonts/fa-regular-400.woff2
  75. BIN
      src/main/resources/static/assets/font-awesome-6.5.1/webfonts/fa-solid-900.ttf
  76. BIN
      src/main/resources/static/assets/font-awesome-6.5.1/webfonts/fa-solid-900.woff2
  77. BIN
      src/main/resources/static/assets/font-awesome-6.5.1/webfonts/fa-v4compatibility.ttf
  78. BIN
      src/main/resources/static/assets/font-awesome-6.5.1/webfonts/fa-v4compatibility.woff2
  79. 7023
      src/main/resources/static/assets/font-awesome-6.5.2/css/all.min.css
  80. BIN
      src/main/resources/static/assets/font-awesome-6.5.2/webfonts/fa-brands-400.ttf
  81. BIN
      src/main/resources/static/assets/font-awesome-6.5.2/webfonts/fa-brands-400.woff2
  82. BIN
      src/main/resources/static/assets/font-awesome-6.5.2/webfonts/fa-regular-400.ttf
  83. BIN
      src/main/resources/static/assets/font-awesome-6.5.2/webfonts/fa-regular-400.woff2
  84. BIN
      src/main/resources/static/assets/font-awesome-6.5.2/webfonts/fa-solid-900.ttf
  85. BIN
      src/main/resources/static/assets/font-awesome-6.5.2/webfonts/fa-solid-900.woff2
  86. BIN
      src/main/resources/static/assets/font-awesome-6.5.2/webfonts/fa-v4compatibility.ttf
  87. BIN
      src/main/resources/static/assets/font-awesome-6.5.2/webfonts/fa-v4compatibility.woff2
  88. BIN
      src/main/resources/static/assets/header-images/gcc1.png
  89. 3244
      src/main/resources/static/assets/jquery-3.6.4/js/jquery-3.6.4.min.js
  90. 93
      src/main/resources/static/assets/js/add-url-data-script.js
  91. 20
      src/main/resources/static/assets/js/fetch-csrf-script.js
  92. 3
      src/main/resources/static/assets/js/go-back-script.js
  93. 420
      src/main/resources/static/assets/js/home-script.js
  94. 28
      src/main/resources/static/assets/js/login-script.js
  95. 136
      src/main/resources/static/assets/js/update-url-data-script.js
  96. 56
      src/main/resources/static/assets/js/view-login-details-script.js
  97. 1123
      src/main/resources/static/assets/popper-2.11.8/js/popper.min.js
  98. BIN
      src/main/resources/static/assets/poppins-23.0/fonts/devanagari/poppins-v23-devanagari-100.woff2
  99. BIN
      src/main/resources/static/assets/poppins-23.0/fonts/devanagari/poppins-v23-devanagari-200.woff2
  100. BIN
      src/main/resources/static/assets/poppins-23.0/fonts/devanagari/poppins-v23-devanagari-300.woff2

2
.gitattributes

@ -0,0 +1,2 @@
/mvnw text eol=lf
*.cmd text eol=crlf

33
.gitignore

@ -0,0 +1,33 @@
HELP.md
target/
.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/

3
.mvn/wrapper/maven-wrapper.properties

@ -0,0 +1,3 @@
wrapperVersion=3.3.4
distributionType=only-script
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip

295
mvnw

@ -0,0 +1,295 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# 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.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Apache Maven Wrapper startup batch script, version 3.3.4
#
# Optional ENV vars
# -----------------
# JAVA_HOME - location of a JDK home dir, required when download maven via java source
# MVNW_REPOURL - repo url base for downloading maven distribution
# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
# ----------------------------------------------------------------------------
set -euf
[ "${MVNW_VERBOSE-}" != debug ] || set -x
# OS specific support.
native_path() { printf %s\\n "$1"; }
case "$(uname)" in
CYGWIN* | MINGW*)
[ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
native_path() { cygpath --path --windows "$1"; }
;;
esac
# set JAVACMD and JAVACCMD
set_java_home() {
# For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
if [ -n "${JAVA_HOME-}" ]; then
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACCMD="$JAVA_HOME/jre/sh/javac"
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACCMD="$JAVA_HOME/bin/javac"
if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
return 1
fi
fi
else
JAVACMD="$(
'set' +e
'unset' -f command 2>/dev/null
'command' -v java
)" || :
JAVACCMD="$(
'set' +e
'unset' -f command 2>/dev/null
'command' -v javac
)" || :
if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
return 1
fi
fi
}
# hash string like Java String::hashCode
hash_string() {
str="${1:-}" h=0
while [ -n "$str" ]; do
char="${str%"${str#?}"}"
h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
str="${str#?}"
done
printf %x\\n $h
}
verbose() { :; }
[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
die() {
printf %s\\n "$1" >&2
exit 1
}
trim() {
# MWRAPPER-139:
# Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
# Needed for removing poorly interpreted newline sequences when running in more
# exotic environments such as mingw bash on Windows.
printf "%s" "${1}" | tr -d '[:space:]'
}
scriptDir="$(dirname "$0")"
scriptName="$(basename "$0")"
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
while IFS="=" read -r key value; do
case "${key-}" in
distributionUrl) distributionUrl=$(trim "${value-}") ;;
distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
esac
done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties"
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
case "${distributionUrl##*/}" in
maven-mvnd-*bin.*)
MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
*AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
:Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
:Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
:Linux*x86_64*) distributionPlatform=linux-amd64 ;;
*)
echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
distributionPlatform=linux-amd64
;;
esac
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
;;
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
esac
# apply MVNW_REPOURL and calculate MAVEN_HOME
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
distributionUrlName="${distributionUrl##*/}"
distributionUrlNameMain="${distributionUrlName%.*}"
distributionUrlNameMain="${distributionUrlNameMain%-bin}"
MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
exec_maven() {
unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
}
if [ -d "$MAVEN_HOME" ]; then
verbose "found existing MAVEN_HOME at $MAVEN_HOME"
exec_maven "$@"
fi
case "${distributionUrl-}" in
*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
esac
# prepare tmp dir
if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
trap clean HUP INT TERM EXIT
else
die "cannot create temp dir"
fi
mkdir -p -- "${MAVEN_HOME%/*}"
# Download and Install Apache Maven
verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
verbose "Downloading from: $distributionUrl"
verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
# select .zip or .tar.gz
if ! command -v unzip >/dev/null; then
distributionUrl="${distributionUrl%.zip}.tar.gz"
distributionUrlName="${distributionUrl##*/}"
fi
# verbose opt
__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
# normalize http auth
case "${MVNW_PASSWORD:+has-password}" in
'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
esac
if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
verbose "Found wget ... using wget"
wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
verbose "Found curl ... using curl"
curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
elif set_java_home; then
verbose "Falling back to use Java to download"
javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
cat >"$javaSource" <<-END
public class Downloader extends java.net.Authenticator
{
protected java.net.PasswordAuthentication getPasswordAuthentication()
{
return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
}
public static void main( String[] args ) throws Exception
{
setDefault( new Downloader() );
java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
}
}
END
# For Cygwin/MinGW, switch paths to Windows format before running javac and java
verbose " - Compiling Downloader.java ..."
"$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
verbose " - Running Downloader.java ..."
"$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
fi
# If specified, validate the SHA-256 sum of the Maven distribution zip file
if [ -n "${distributionSha256Sum-}" ]; then
distributionSha256Result=false
if [ "$MVN_CMD" = mvnd.sh ]; then
echo "Checksum validation is not supported for maven-mvnd." >&2
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
exit 1
elif command -v sha256sum >/dev/null; then
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then
distributionSha256Result=true
fi
elif command -v shasum >/dev/null; then
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
distributionSha256Result=true
fi
else
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
exit 1
fi
if [ $distributionSha256Result = false ]; then
echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
exit 1
fi
fi
# unzip and move
if command -v unzip >/dev/null; then
unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
else
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
fi
# Find the actual extracted directory name (handles snapshots where filename != directory name)
actualDistributionDir=""
# First try the expected directory name (for regular distributions)
if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then
if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then
actualDistributionDir="$distributionUrlNameMain"
fi
fi
# If not found, search for any directory with the Maven executable (for snapshots)
if [ -z "$actualDistributionDir" ]; then
# enable globbing to iterate over items
set +f
for dir in "$TMP_DOWNLOAD_DIR"/*; do
if [ -d "$dir" ]; then
if [ -f "$dir/bin/$MVN_CMD" ]; then
actualDistributionDir="$(basename "$dir")"
break
fi
fi
done
set -f
fi
if [ -z "$actualDistributionDir" ]; then
verbose "Contents of $TMP_DOWNLOAD_DIR:"
verbose "$(ls -la "$TMP_DOWNLOAD_DIR")"
die "Could not find Maven distribution directory in extracted archive"
fi
verbose "Found extracted Maven distribution directory: $actualDistributionDir"
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url"
mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
clean || :
exec_maven "$@"

189
mvnw.cmd

@ -0,0 +1,189 @@
<# : batch portion
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Apache Maven Wrapper startup batch script, version 3.3.4
@REM
@REM Optional ENV vars
@REM MVNW_REPOURL - repo url base for downloading maven distribution
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
@REM ----------------------------------------------------------------------------
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
@SET __MVNW_CMD__=
@SET __MVNW_ERROR__=
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
@SET PSModulePath=
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
)
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
@SET __MVNW_PSMODULEP_SAVE=
@SET __MVNW_ARG0_NAME__=
@SET MVNW_USERNAME=
@SET MVNW_PASSWORD=
@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*)
@echo Cannot start maven from wrapper >&2 && exit /b 1
@GOTO :EOF
: end batch / begin powershell #>
$ErrorActionPreference = "Stop"
if ($env:MVNW_VERBOSE -eq "true") {
$VerbosePreference = "Continue"
}
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
if (!$distributionUrl) {
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
}
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
"maven-mvnd-*" {
$USE_MVND = $true
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
$MVN_CMD = "mvnd.cmd"
break
}
default {
$USE_MVND = $false
$MVN_CMD = $script -replace '^mvnw','mvn'
break
}
}
# apply MVNW_REPOURL and calculate MAVEN_HOME
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
if ($env:MVNW_REPOURL) {
$MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" }
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')"
}
$distributionUrlName = $distributionUrl -replace '^.*/',''
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
$MAVEN_M2_PATH = "$HOME/.m2"
if ($env:MAVEN_USER_HOME) {
$MAVEN_M2_PATH = "$env:MAVEN_USER_HOME"
}
if (-not (Test-Path -Path $MAVEN_M2_PATH)) {
New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null
}
$MAVEN_WRAPPER_DISTS = $null
if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) {
$MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists"
} else {
$MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists"
}
$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain"
$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
exit $?
}
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
}
# prepare tmp dir
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
trap {
if ($TMP_DOWNLOAD_DIR.Exists) {
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
}
}
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
# Download and Install Apache Maven
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
Write-Verbose "Downloading from: $distributionUrl"
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
$webclient = New-Object System.Net.WebClient
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
# If specified, validate the SHA-256 sum of the Maven distribution zip file
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
if ($distributionSha256Sum) {
if ($USE_MVND) {
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
}
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
}
}
# unzip and move
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
# Find the actual extracted directory name (handles snapshots where filename != directory name)
$actualDistributionDir = ""
# First try the expected directory name (for regular distributions)
$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain"
$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD"
if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) {
$actualDistributionDir = $distributionUrlNameMain
}
# If not found, search for any directory with the Maven executable (for snapshots)
if (!$actualDistributionDir) {
Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object {
$testPath = Join-Path $_.FullName "bin/$MVN_CMD"
if (Test-Path -Path $testPath -PathType Leaf) {
$actualDistributionDir = $_.Name
}
}
}
if (!$actualDistributionDir) {
Write-Error "Could not find Maven distribution directory in extracted archive"
}
Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir"
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null
try {
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
} catch {
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
Write-Error "fail to move MAVEN_HOME"
}
} finally {
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
}
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"

140
pom.xml

@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<!-- <version>3.5.7</version> -->
<version>2.5.4</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.gcc_it_cell_services</groupId>
<artifactId>gcc-it-cell-services</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>gcc-it-cell-services</name>
<description>Springboot Project For GCC IT cell services</description>
<url />
<licenses>
<license />
</licenses>
<developers>
<developer />
</developers>
<scm>
<connection />
<developerConnection />
<tag />
<url />
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc11</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version> <!-- Replace with the appropriate version -->
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
<version>1.18.32</version>
</dependency>
<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>bucket4j-core</artifactId>
<version>7.6.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<!-- <dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>2.2.1</version>
</dependency> -->
<!-- <dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>2.0.1</version>
</dependency> -->
</dependencies>
<build>
<finalName>gcc-it-cell-services</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<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>
</plugins>
</build>
</project>

17
src/main/java/com/gcc_it_cell_services/springboot/GccItCellServicesApplication.java

@ -0,0 +1,17 @@
package com.gcc_it_cell_services.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableJpaAuditing(auditorAwareRef = "auditorAware")
@EnableScheduling
public class GccItCellServicesApplication {
public static void main(String[] args) {
SpringApplication.run(GccItCellServicesApplication.class, args);
}
}

41
src/main/java/com/gcc_it_cell_services/springboot/configuration/AuditorAwareConfiguration.java

@ -0,0 +1,41 @@
package com.gcc_it_cell_services.springboot.configuration;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.AuditorAware;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.gcc_it_cell_services.springboot.model.AppUser;
import com.gcc_it_cell_services.springboot.repository.AppUserRepository;
@Component("auditorAware")
public class AuditorAwareConfiguration implements AuditorAware<AppUser> {
@Autowired
private AppUserRepository appUsrRepo;
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
public Optional<AppUser> getCurrentAuditor() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
return Optional.empty();
}
// If it's already AppUser, no need to hit DB
Object principal = authentication.getPrincipal();
if (principal instanceof AppUser user) {
return Optional.of(user);
}
// Fetch AppUser from DB using username
String username = authentication.getName();
return appUsrRepo.findByUsrName(username);
}
}

42
src/main/java/com/gcc_it_cell_services/springboot/configuration/RateLimitConfiguration.java

@ -0,0 +1,42 @@
package com.gcc_it_cell_services.springboot.configuration;
import java.time.Duration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.context.annotation.Configuration;
import io.github.bucket4j.Bandwidth;
import io.github.bucket4j.Bucket;
@Configuration
public class RateLimitConfiguration {
// Map to store buckets for each key
private final Map<String, Bucket> rateLimitCache = new ConcurrentHashMap<String, Bucket>();
public Bucket resolveBucket(String key) {
return rateLimitCache.computeIfAbsent(key, newKey -> createNewBucket(newKey));
}
private Bucket createNewBucket(String key) {
// Set different limits based on key type
if (key.startsWith("OTP_")) {
// OTP = 5 req/min
return Bucket.builder().addLimit(Bandwidth.simple(5, Duration.ofMinutes(1))).build();
} else if (key.startsWith("LOGIN_")) {
// Login = 5 req/min
return Bucket.builder().addLimit(Bandwidth.simple(5, Duration.ofMinutes(1))).build();
} else if (key.startsWith("IP_")) {
// Public IP = 35 req/min
return Bucket.builder().addLimit(Bandwidth.simple(35, Duration.ofMinutes(1))).build();
} else if (key.startsWith("USER_")) {
// Authenticated = 50/min
return Bucket.builder().addLimit(Bandwidth.simple(35, Duration.ofMinutes(1))).build();
}
// Default fallback
return Bucket.builder().addLimit(Bandwidth.simple(40, Duration.ofMinutes(1))).build();
}
}

136
src/main/java/com/gcc_it_cell_services/springboot/configuration/SecurityConfiguration.java

@ -0,0 +1,136 @@
package com.gcc_it_cell_services.springboot.configuration;
import java.util.List;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import com.gcc_it_cell_services.springboot.service.HomeService;
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
/*
* @Value("${app.security.force-https}") private boolean isForceHttps;
*/
@Autowired
private HomeService homeSer;
// List of all admin-only endpoints
private final List<String> adminEndpoints = List.of("/urls/add-url-data", "/urls/upd-url-data", "/api/urls/**");
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
// Return 404 for unauthenticated requests to admin endpoints
private final AuthenticationEntryPoint notFoundEntryPoint = (req, res, authException) -> {
if (adminEndpoints.contains(req.getRequestURI())) {
res.sendError(404); // hide existence
} else {
res.sendRedirect(req.getContextPath() + "/login"); // fallback for login for other endpoints
}
};
// Return 404 for access denied on admin endpoints
private final AccessDeniedHandler adminNotFoundAccessDeniedHandler = (req, res, accessDeniedException) -> {
if (adminEndpoints.contains(req.getRequestURI())) {
res.sendError(404); // hide existence
} else {
res.sendRedirect(req.getContextPath() + "/login"); // fallback for login for other endpoints
}
};
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12);
}
@Bean
public DaoAuthenticationProvider daoAuthProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(homeSer);
provider.setPasswordEncoder(passwordEncoder());
provider.setHideUserNotFoundExceptions(false); // THIS FIXES USER NOT FOUND EXCEPTION ISSUE
return provider;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthProvider()); // use custom provider
}
// disable default login form
@Override
protected void configure(HttpSecurity http) throws Exception {
// CSRF with Cookie token repository
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
// Force HTTPS if enabled
// if (isForceHttps) {
/* http.redirectToHttps(withDefaults()); */
// http.requiresChannel().anyRequest().requiresSecure();
// }
// Authorization rules
http.authorizeRequests().antMatchers("/urls/add-url-data", "/urls/upd-url-data", "/api/urls/**")
.hasRole("ADMIN") // Admin-only
.antMatchers("/api/val-app-usr-login", "/api/get-role", "/api/get-otp", "/api/verify-otp",
"/api/get-mob-num", "/clr-otp-ses", "/", "/redirect", "/login", "/view-login-det",
"/get-{urlCatName}-urls", "/usr-logout")
.permitAll() // Publicly accessible
.anyRequest().permitAll(); // Requires authentication for any other path
// Session management
http.sessionManagement().sessionFixation().migrateSession() // Prevent session fixation attacks
.invalidSessionUrl("/") // Handle session expiration
.maximumSessions(2).maxSessionsPreventsLogin(true).and()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED); // Limit concurrent logins
// Exception handling
http.exceptionHandling().authenticationEntryPoint(notFoundEntryPoint)
.accessDeniedHandler(adminNotFoundAccessDeniedHandler);
// Custom login/logout pages
http.formLogin().loginPage("/login").permitAll().failureUrl("/login"); // Redirect on failed login
http.logout().logoutUrl("/usr-logout").logoutSuccessUrl("/").invalidateHttpSession(true)
.clearAuthentication(true).permitAll();
}
// Set the session timeout duration programmatically
@Bean
public HttpSessionListener httpSessionListener() {
return new HttpSessionListener() {
@Override
public void sessionCreated(HttpSessionEvent event) {
// Set the timeout period (e.g., 30 minutes)
event.getSession().setMaxInactiveInterval(30 * 60); // 30 minutes
}
@Override
public void sessionDestroyed(HttpSessionEvent event) {
// No specific action on session destruction
}
};
}
}

37
src/main/java/com/gcc_it_cell_services/springboot/configuration/WebConfiguration.java

@ -0,0 +1,37 @@
package com.gcc_it_cell_services.springboot.configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.gcc_it_cell_services.springboot.interceptor.RateLimitInterceptor;
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
@Autowired
private RateLimitInterceptor rateLimitInterceptor;
@Override
public void addInterceptors(InterceptorRegistry interceptorRegistry) {
// Fetch the application context path dynamically
String contextPath = System.getProperty("server.servlet.context-path", "");
// Define all paths for rate-limiting
String[] apiPaths = { "/api/**" };
String[] publicPaths = { "/", "/login", "/view-login-det", "/redirect", "/urls/get-**" };
// Prepend context path dynamically
for (int i = 0; i < apiPaths.length; i++) {
apiPaths[i] = contextPath + apiPaths[i];
}
for (int i = 0; i < publicPaths.length; i++) {
publicPaths[i] = contextPath + publicPaths[i];
}
// Register interceptor for API + public paths
interceptorRegistry.addInterceptor(rateLimitInterceptor).addPathPatterns(apiPaths).addPathPatterns(publicPaths);
}
}

70
src/main/java/com/gcc_it_cell_services/springboot/controller/HomeController.java

@ -0,0 +1,70 @@
package com.gcc_it_cell_services.springboot.controller;
import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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 com.gcc_it_cell_services.springboot.response_dto.UrlDataResponseDTO;
import com.gcc_it_cell_services.springboot.service.UrlDataService;
@Controller
@RequestMapping("/")
public class HomeController {
@Autowired
private UrlDataService urlDataSer;
@GetMapping({ "/", "" })
public String home(Model model, Authentication auth) {
List<UrlDataResponseDTO> urls = urlDataSer.getAllUrls();
// Add role information to the model for conditional navbar rendering
boolean isAdmin = auth != null
&& auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"));
model.addAttribute("isAdmin", isAdmin);
model.addAttribute("curPage", "home");
model.addAttribute("urls", urls);
return "home/home";
}
@GetMapping("/login")
public String login() {
return "home/login";
}
@GetMapping("/view-login-det")
public String viewLoginDet(Model model, Authentication auth) {
// Add role information to the model for conditional navbar rendering
boolean isAdmin = auth != null
&& auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"));
model.addAttribute("isAdmin", isAdmin);
List<UrlDataResponseDTO> urls = urlDataSer.getAllUrlsWithUsrNameAndPass();
model.addAttribute("urls", urls);
model.addAttribute("curPage", "view-login-det");
return "home/view-login-details";
}
@GetMapping("/redirect")
public void usrRedirect(Authentication auth, HttpServletResponse res, HttpServletRequest req) throws IOException {
if (auth == null || !auth.isAuthenticated()) {
res.sendError(404); // hide endpoint if not logged in
return;
}
boolean isAdmin = auth != null
&& auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"));
if (isAdmin) {
res.sendRedirect(req.getContextPath() + "/"); // admin goes to home
} else {
res.sendError(404); // non-admin users → hide endpoint
}
}
}

61
src/main/java/com/gcc_it_cell_services/springboot/controller/UrlDataController.java

@ -0,0 +1,61 @@
package com.gcc_it_cell_services.springboot.controller;
import java.util.List;
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.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import com.gcc_it_cell_services.springboot.response_dto.StatusResponseDTO;
import com.gcc_it_cell_services.springboot.response_dto.UrlDataResponseDTO;
import com.gcc_it_cell_services.springboot.service.UrlDataService;
@Controller
@RequestMapping({ "/urls", "/itcell/urls" })
public class UrlDataController {
@Autowired
private UrlDataService urlDataSer;
@GetMapping("/add-url-data")
public String addUrl(Model model, Authentication auth) {
// Add role information to the model for conditional navbar rendering
boolean isAdmin = auth != null
&& auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"));
model.addAttribute("isAdmin", isAdmin);
model.addAttribute("curPage", "add-url");
return "urls/add-url-data";
}
@GetMapping("/get-{urlCatName}-urls")
public String getUrlsByCategory(@PathVariable("urlCatName") String urlCatName, Model model, Authentication auth) {
// Add role information to the model for conditional navbar rendering
urlCatName = urlCatName.replace("-", " ");
List<UrlDataResponseDTO> urls = urlDataSer.getUrlsByUrlCatName(urlCatName);
urlCatName = urls.get(0).getUrlCatResDTO().getUrlCatName();
boolean isAdmin = auth != null
&& auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"));
model.addAttribute("isAdmin", isAdmin);
model.addAttribute("urlCatName", urlCatName);
model.addAttribute("urls", urls);
return "urls/get-urls-by-url-category"; // Thymeleaf view name
}
@GetMapping("/upd-url-data")
public String updUrl(Model model, Authentication auth) {
// Add role information to the model for conditional navbar rendering
List<UrlDataResponseDTO> urls = urlDataSer.getAllUrlsSortedByUrlName();
List<StatusResponseDTO> stats = urlDataSer.getAllStats();
boolean isAdmin = auth != null
&& auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"));
model.addAttribute("isAdmin", isAdmin);
model.addAttribute("curPage", "upd-url");
model.addAttribute("stats", stats);
model.addAttribute("urls", urls);
return "urls/update-url-data";
}
}

102
src/main/java/com/gcc_it_cell_services/springboot/interceptor/RateLimitInterceptor.java

@ -0,0 +1,102 @@
package com.gcc_it_cell_services.springboot.interceptor;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gcc_it_cell_services.springboot.configuration.RateLimitConfiguration;
import com.gcc_it_cell_services.springboot.util.MobileNumberTokenStore;
import io.github.bucket4j.Bucket;
@Component
public class RateLimitInterceptor implements HandlerInterceptor {
private final RateLimitConfiguration rateLimitConfig;
private final MobileNumberTokenStore mobNumTokenStore;
public RateLimitInterceptor(RateLimitConfiguration rateLimitConfig, MobileNumberTokenStore mobNumTokenStore) {
this.rateLimitConfig = rateLimitConfig;
this.mobNumTokenStore = mobNumTokenStore;
}
@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception {
String key = extractKey(req);
Bucket bucket = rateLimitConfig.resolveBucket(key);
if (bucket.tryConsume(1)) {
// allow request
return true;
}
res.setStatus(429);
res.setContentType("application/json"); // <--- important
Map<String, Object> resObj = new HashMap<String, Object>();
resObj.put("result", false);
resObj.put("error", "Too many requests - Please try after some time");
// Write JSON
ObjectMapper mapper = new ObjectMapper();
res.getWriter().write(mapper.writeValueAsString(resObj));
return false;
}
private String extractKey(HttpServletRequest req) {
String uri = req.getRequestURI();
String contextPath = req.getContextPath();
if (uri.startsWith(contextPath)) {
uri = uri.substring(contextPath.length());
}
// OTP endpoints → per mobile number
if (uri.matches("/api/(get|verify)-otp")) {
String mobNumId = req.getParameter("mobNumId");
if (mobNumId != null && !mobNumId.trim().isBlank()) {
// Retrieve actual mobile number from MobileNumberTokenStore
String mobNum = mobNumTokenStore.getMobNum(mobNumId);
if (mobNum != null && !mobNum.trim().isBlank()) {
return "OTP_" + mobNum;
}
}
}
// Login endpoint → per username
if (uri.contains("/api/val-app-usr-login")) {
String usrName = req.getParameter("usrName");
if (usrName != null && !usrName.trim().isBlank()) {
return "LOGIN_" + usrName;
}
}
// Admin endpoints → per authenticated username
if (uri.startsWith("/urls/") || uri.startsWith("/api/urls/") || uri.startsWith("/itcell/urls/")) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.isAuthenticated() && !"anonymousUser".equals(auth.getName())) {
return "USER_" + auth.getName();
}
}
// Authenticated endpoints → per username
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.isAuthenticated() && !"anonymousUser".equals(auth.getName())) {
return "USER_" + auth.getName();
}
// Dynamic public endpoints → fallback to IP
if (uri.matches("/urls/get-[a-zA-Z0-9\\-]+-urls")) {
return "IP_" + req.getRemoteAddr();
}
// Public endpoints → fallback to IP
return "IP_" + req.getRemoteAddr();
}
}

38
src/main/java/com/gcc_it_cell_services/springboot/mapper/OtpLogMapper.java

@ -0,0 +1,38 @@
package com.gcc_it_cell_services.springboot.mapper;
import org.springframework.stereotype.Component;
import com.gcc_it_cell_services.springboot.model.OtpLog;
import com.gcc_it_cell_services.springboot.request_dto.OtpLogRequestDTO;
import com.gcc_it_cell_services.springboot.response_dto.OtpLogResponseDTO;
@Component
public class OtpLogMapper {
public OtpLog toOtpLogEnt(OtpLogRequestDTO otpLogReqDTO) {
OtpLog otpLog = new OtpLog();
otpLog.setOtp(otpLogReqDTO.getOtp());
otpLog.setOtpStat(otpLogReqDTO.getOtpStat());
otpLog.setOtpReqMobNum(otpLogReqDTO.getOtpReqMobNum());
otpLog.setOtpResMobNum(otpLogReqDTO.getOtpResMobNum());
otpLog.setOtpLogMsg(otpLogReqDTO.getOtpLogMsg());
return otpLog;
}
public OtpLog toOtpLogEnt(OtpLogResponseDTO otpLogResDTO) {
OtpLog otpLog = new OtpLog();
otpLog.setLogId(otpLogResDTO.getLogId());
otpLog.setOtp(otpLogResDTO.getOtp());
otpLog.setOtpLogMsg(otpLogResDTO.getOtpLogMsg());
otpLog.setOtpReqMobNum(otpLogResDTO.getOtpReqMobNum());
otpLog.setOtpResMobNum(otpLogResDTO.getOtpResMobNum());
otpLog.setOtpStat(otpLogResDTO.getOtpStat());
return otpLog;
}
public OtpLogResponseDTO toOtpLogResDTO(OtpLog otpLog) {
return new OtpLogResponseDTO(otpLog.getLogId(), otpLog.getOtp(), otpLog.getOtpStat(), otpLog.getOtpReqMobNum(),
otpLog.getOtpResMobNum(), otpLog.getOtpLogMsg());
}
}

19
src/main/java/com/gcc_it_cell_services/springboot/mapper/StatusMapper.java

@ -0,0 +1,19 @@
package com.gcc_it_cell_services.springboot.mapper;
import org.springframework.stereotype.Component;
import com.gcc_it_cell_services.springboot.model.Status;
import com.gcc_it_cell_services.springboot.response_dto.StatusResponseDTO;
@Component
public class StatusMapper {
public StatusResponseDTO toStatResDTO(Status stat) {
return new StatusResponseDTO(stat.getStatId(), stat.getStatName());
}
public Status toStatEnt(StatusResponseDTO statResDTO) {
return new Status(statResDTO.getStatId(), statResDTO.getStatName());
}
}

29
src/main/java/com/gcc_it_cell_services/springboot/mapper/UrlCategoryMapper.java

@ -0,0 +1,29 @@
package com.gcc_it_cell_services.springboot.mapper;
import org.springframework.stereotype.Component;
import com.gcc_it_cell_services.springboot.model.UrlCategory;
import com.gcc_it_cell_services.springboot.request_dto.UrlCategoryRequestDTO;
import com.gcc_it_cell_services.springboot.response_dto.UrlCategoryResponseDTO;
@Component
public class UrlCategoryMapper {
public UrlCategoryResponseDTO toUrlCatResDTO(UrlCategory urlCat) {
return new UrlCategoryResponseDTO(urlCat.getUrlCatId(), urlCat.getUrlCatName());
}
public UrlCategory toUrlCatEnt(UrlCategoryRequestDTO urlCatReqDTO) {
UrlCategory urlCat = new UrlCategory();
urlCat.setUrlCatName(urlCatReqDTO.getUrlCatName());
return urlCat;
}
public UrlCategory toUrlCatEnt(UrlCategoryResponseDTO urlCatResDTO) {
UrlCategory urlCat = new UrlCategory();
urlCat.setUrlCatId(urlCatResDTO.getUrlCatId());
urlCat.setUrlCatName(urlCatResDTO.getUrlCatName());
return urlCat;
}
}

48
src/main/java/com/gcc_it_cell_services/springboot/mapper/UrlDataMapper.java

@ -0,0 +1,48 @@
package com.gcc_it_cell_services.springboot.mapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.gcc_it_cell_services.springboot.model.UrlData;
import com.gcc_it_cell_services.springboot.request_dto.UrlDataRequestDTO;
import com.gcc_it_cell_services.springboot.response_dto.UrlDataResponseDTO;
@Component
public class UrlDataMapper {
@Autowired
private UrlCategoryMapper urlCatMap;
@Autowired
private StatusMapper statMap;
public UrlData toUrlDatEnt(UrlDataRequestDTO urlDataReqDTO) {
UrlData urlData = new UrlData();
urlData.setUrlDataLink(urlDataReqDTO.getUrlDataLink());
urlData.setUrlDataName(urlDataReqDTO.getUrlDataName());
urlData.setUrlDataUsrName(urlDataReqDTO.getUrlDataUsrName());
urlData.setUrlDataUsrPass(urlDataReqDTO.getUrlDataUsrPass());
urlData.setUrlCat(urlCatMap.toUrlCatEnt(urlDataReqDTO.getUrlCatResDTO()));
urlData.setStatId(statMap.toStatEnt(urlDataReqDTO.getStatResDTO()));
return urlData;
}
public UrlDataResponseDTO toUrlDataResDTO(UrlData urlData) {
return new UrlDataResponseDTO(urlData.getUrlDataId(), urlData.getUrlDataName(), urlData.getUrlDataLink(),
urlData.getUrlDataUsrPass(), urlData.getUrlDataUsrName(), urlCatMap.toUrlCatResDTO(urlData.getUrlCat()),
statMap.toStatResDTO(urlData.getStatId()));
}
public UrlData toUrlDatEnt(UrlDataResponseDTO urlDataResDTO) {
// TODO Auto-generated method stub
UrlData urlData = new UrlData();
urlData.setUrlDataId(urlDataResDTO.getUrlDataId());
urlData.setUrlDataLink(urlDataResDTO.getUrlDataLink());
urlData.setUrlDataName(urlDataResDTO.getUrlDataName());
urlData.setUrlDataUsrName(urlDataResDTO.getUrlDataUsrName());
urlData.setUrlDataUsrPass(urlDataResDTO.getUrlDataUsrPass());
urlData.setUrlCat(urlCatMap.toUrlCatEnt(urlDataResDTO.getUrlCatResDTO()));
urlData.setStatId(statMap.toStatEnt(urlDataResDTO.getStatResDTO()));
return urlData;
}
}

70
src/main/java/com/gcc_it_cell_services/springboot/model/AppUser.java

@ -0,0 +1,70 @@
package com.gcc_it_cell_services.springboot.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.validation.constraints.NotBlank;
import com.fasterxml.jackson.annotation.JsonBackReference;
// import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Entity
// @Table(name = "tbl_app_usr_det_2", schema = "fileadjust")
@Table(name = "tbl_app_usr_det")
/* @Table(name = "tbl_app_usr_det", schema = "gcc") */
@Getter
@Setter
@ToString(doNotUseGetters = true, includeFieldNames = true, exclude = { "usrRole", "usrPass", "mobStatId",
"usrStatId" })
@NoArgsConstructor
@AllArgsConstructor
public class AppUser {
@Id
@Column(name = "usr_id", nullable = false, updatable = false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
/*
* @GeneratedValue(strategy = GenerationType.SEQUENCE, generator =
* "app_usr_seq_2")
*
* @SequenceGenerator(name = "app_usr_seq_2", sequenceName = "app_usr_id_seq_2",
* allocationSize = 1)
*/
private Integer usrId;
@Column(name = "usr_name", nullable = false)
@NotBlank
private String usrName;
@Column(name = "usr_pass", nullable = false)
@NotBlank
private String usrPass;
@Column(name = "usr_mob_num", nullable = false)
@NotBlank
private String usrMobNum;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "mob_stat_id", referencedColumnName = "stat_id", nullable = false)
private Status mobStatId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "usr_stat_id", referencedColumnName = "stat_id", nullable = false)
private Status usrStatId;
@ManyToOne
@JoinColumn(name = "usr_role_id", nullable = false, referencedColumnName = "usr_role_id")
@JsonBackReference
private UserRole usrRole;
}

102
src/main/java/com/gcc_it_cell_services/springboot/model/OtpLog.java

@ -0,0 +1,102 @@
package com.gcc_it_cell_services.springboot.model;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "tbl_otp_log_det")
@Getter
@Setter
@ToString(doNotUseGetters = true, includeFieldNames = true)
@AllArgsConstructor
@NoArgsConstructor
public class OtpLog {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "log_id", nullable = false, updatable = false)
private Integer logId;
@Column(name = "otp", nullable = false, updatable = false)
@NotNull
private Integer otp;
@Column(name = "otp_stat", nullable = false)
@NotBlank
private String otpStat;
@Column(name = "otp_req_mob_num", nullable = false, updatable = false)
@NotBlank
private String otpReqMobNum;
@Column(name = "otp_res_mob_num", nullable = false)
@NotBlank
private String otpResMobNum;
@Column(name = "otp_log_msg", nullable = false)
@NotBlank
private String otpLogMsg;
// User timestamp
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "usr_req_timestamp", nullable = false, updatable = false, columnDefinition = "TIMESTAMP(0)")
private Date usrReqTimestamp;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "usr_res_timestamp", nullable = false, columnDefinition = "TIMESTAMP(0)")
private Date usrResTimestamp;
// DB-managed timestamps
@CreatedDate
@Temporal(TemporalType.TIMESTAMP)
@Column(updatable = false, nullable = false, name = "db_req_timestamp", columnDefinition = "TIMESTAMP(0)")
private Date dbReqTimestamp;
@LastModifiedDate
@Temporal(TemporalType.TIMESTAMP)
@Column(nullable = false, name = "db_res_timestamp", columnDefinition = "TIMESTAMP(0)")
private Date dbResTimestamp;
@PrePersist
public void onCreate() {
Date now = Date.from(Instant.now().truncatedTo(ChronoUnit.SECONDS));
this.usrReqTimestamp = now;
this.usrResTimestamp = now;
this.dbReqTimestamp = now;
this.dbResTimestamp = now;
}
@PreUpdate
public void onUpdate() {
Date now = Date.from(Instant.now().truncatedTo(ChronoUnit.SECONDS));
this.usrResTimestamp = now;
this.dbResTimestamp = now;
}
}

35
src/main/java/com/gcc_it_cell_services/springboot/model/Status.java

@ -0,0 +1,35 @@
package com.gcc_it_cell_services.springboot.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Entity
@ToString(includeFieldNames = true, doNotUseGetters = true)
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "tbl_stat_det")
public class Status {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "stat_id", nullable = false, updatable = false)
private Long statId;
@Column(name = "stat_name", nullable = false)
@NotBlank
private String statName;
}

106
src/main/java/com/gcc_it_cell_services/springboot/model/UrlCategory.java

@ -0,0 +1,106 @@
package com.gcc_it_cell_services.springboot.model;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotBlank;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Entity
@Table(name = "tbl_url_cat_det")
@Getter
@Setter
@ToString(includeFieldNames = true, doNotUseGetters = true, exclude = { "urlDatas", "createdBy", "updatedBy" })
@AllArgsConstructor
@NoArgsConstructor
@EntityListeners(AuditingEntityListener.class)
public class UrlCategory {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "url_cat_id", nullable = false, updatable = false)
private Long urlCatId;
@Column(name = "url_cat_name", nullable = false)
@NotBlank
private String urlCatName;
@OneToMany(mappedBy = "urlCat", orphanRemoval = true, cascade = CascadeType.ALL)
@JsonManagedReference
private List<UrlData> urlDatas;
@CreatedBy
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "created_by_usr_id", referencedColumnName = "usr_id", updatable = false, nullable = false)
private AppUser createdBy;
@LastModifiedBy
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "updated_by_usr_id", referencedColumnName = "usr_id", nullable = false)
private AppUser updatedBy;
// User timestamp
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "usr_created_timestamp", nullable = false, updatable = false, columnDefinition = "TIMESTAMP(0)")
private Date usrCreatedTimestamp;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "usr_updated_timestamp", nullable = false, columnDefinition = "TIMESTAMP(0)")
private Date usrUpdatedTimestamp;
// DB-managed timestamps
@CreatedDate
@Temporal(TemporalType.TIMESTAMP)
@Column(updatable = false, nullable = false, name = "db_created_timestamp", columnDefinition = "TIMESTAMP(0)")
private Date dbCreatedTimestamp;
@LastModifiedDate
@Temporal(TemporalType.TIMESTAMP)
@Column(nullable = false, name = "db_updated_timestamp", columnDefinition = "TIMESTAMP(0)")
private Date dbUpdatedTimestamp;
@PrePersist
public void onCreate() {
Date now = Date.from(Instant.now().truncatedTo(ChronoUnit.SECONDS));
this.usrCreatedTimestamp = now;
this.usrUpdatedTimestamp = now;
}
@PreUpdate
public void onUpdate() {
Date now = Date.from(Instant.now().truncatedTo(ChronoUnit.SECONDS));
this.usrUpdatedTimestamp = now;
}
}

136
src/main/java/com/gcc_it_cell_services/springboot/model/UrlData.java

@ -0,0 +1,136 @@
package com.gcc_it_cell_services.springboot.model;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotBlank;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import com.fasterxml.jackson.annotation.JsonBackReference;
// import jakarta.persistence.Column;
// import jakarta.persistence.Entity;
// import jakarta.persistence.GeneratedValue;
// import jakarta.persistence.GenerationType;
// import jakarta.persistence.Id;
// import jakarta.persistence.SequenceGenerator;
// import jakarta.persistence.Table;
// import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Entity
@AllArgsConstructor
@NoArgsConstructor
@EntityListeners(AuditingEntityListener.class)
@Getter
@Setter
@ToString(doNotUseGetters = true, includeFieldNames = true, exclude = { "urlCat", "createdBy", "updatedBy", "statId" })
// @Table(name = "tbl_url_data_det", schema = "fileadjust")
@Table(name = "tbl_url_data_det")
public class UrlData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
/*
* @GeneratedValue(strategy = GenerationType.SEQUENCE, generator =
* "url_data_seq")
*
* @SequenceGenerator(name = "url_data_seq", sequenceName = "url_data_id_seq",
* allocationSize = 1)
*/
@Column(name = "url_data_id", nullable = false, updatable = false)
private Long urlDataId;
@Column(name = "url_data_link", nullable = false)
@NotBlank
private String urlDataLink;
@Column(name = "url_data_name", nullable = false)
@NotBlank
private String urlDataName;
@Column(name = "url_data_usr_pass", nullable = false)
@NotBlank
private String urlDataUsrPass;
@Column(name = "url_data_usr_name", nullable = false)
@NotBlank
private String urlDataUsrName;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "url_cat_id", referencedColumnName = "url_cat_id", nullable = false)
@JsonBackReference
private UrlCategory urlCat;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "stat_id", referencedColumnName = "stat_id", nullable = false)
private Status statId;
@CreatedBy
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "created_by_usr_id", referencedColumnName = "usr_id", updatable = false, nullable = false)
private AppUser createdBy;
@LastModifiedBy
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "updated_by_usr_id", referencedColumnName = "usr_id", nullable = false)
private AppUser updatedBy;
// User timestamp
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "usr_created_timestamp", nullable = false, updatable = false, columnDefinition = "TIMESTAMP(0)")
private Date usrCreatedTimestamp;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "usr_updated_timestamp", nullable = false, columnDefinition = "TIMESTAMP(0)")
private Date usrUpdatedTimestamp;
// DB-managed timestamps
@CreatedDate
@Temporal(TemporalType.TIMESTAMP)
@Column(updatable = false, nullable = false, name = "db_created_timestamp", columnDefinition = "TIMESTAMP(0)")
private Date dbCreatedTimestamp;
@LastModifiedDate
@Temporal(TemporalType.TIMESTAMP)
@Column(nullable = false, name = "db_updated_timestamp", columnDefinition = "TIMESTAMP(0)")
private Date dbUpdatedTimestamp;
@PrePersist
public void onCreate() {
Date now = Date.from(Instant.now().truncatedTo(ChronoUnit.SECONDS));
this.usrCreatedTimestamp = now;
this.usrUpdatedTimestamp = now;
}
@PreUpdate
public void onUpdate() {
Date now = Date.from(Instant.now().truncatedTo(ChronoUnit.SECONDS));
this.usrUpdatedTimestamp = now;
}
}

66
src/main/java/com/gcc_it_cell_services/springboot/model/UserRole.java

@ -0,0 +1,66 @@
package com.gcc_it_cell_services.springboot.model;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.NotBlank;
import com.fasterxml.jackson.annotation.JsonManagedReference;
// import jakarta.persistence.CascadeType;
// import jakarta.persistence.Column;
// import jakarta.persistence.Entity;
// import jakarta.persistence.FetchType;
// import jakarta.persistence.GeneratedValue;
// import jakarta.persistence.GenerationType;
// import jakarta.persistence.Id;
// import jakarta.persistence.OneToMany;
// import jakarta.persistence.SequenceGenerator;
// import jakarta.persistence.Table;
// import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Entity
@ToString(includeFieldNames = true, doNotUseGetters = true, exclude = { "appUsrs" })
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
/* @Table(name = "tbl_usr_role_det", schema = "gcc") */
// @Table(name = "tbl_usr_role_det_2", schema = "fileadjust")
@Table(name = "tbl_usr_role_det")
public class UserRole {
@Id
@Column(name = "usr_role_id", nullable = false, updatable = false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
/*
* @GeneratedValue(strategy = GenerationType.SEQUENCE, generator =
* "usr_role_seq_2")
*
* @SequenceGenerator(name = "usr_role_seq_2", sequenceName =
* "usr_role_id_seq_2", allocationSize = 1)
*/
private Integer usrRoleId;
@Column(name = "usr_role_name", nullable = false)
@NotBlank
private String usrRoleName;
@OneToMany(mappedBy = "usrRole", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
@JsonManagedReference
private List<AppUser> appUsrs;
}

25
src/main/java/com/gcc_it_cell_services/springboot/repository/AppUserRepository.java

@ -0,0 +1,25 @@
package com.gcc_it_cell_services.springboot.repository;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import com.gcc_it_cell_services.springboot.model.AppUser;
@Repository
public interface AppUserRepository extends JpaRepository<AppUser, Integer> {
Optional<AppUser> findByUsrName(String usrName);
@Query(value = """
SELECT CASE WHEN COUNT(*) > 0 THEN TRUE ELSE FALSE END FROM tbl_app_usr_det
WHERE usr_mob_num = :mobNum """, nativeQuery = true)
Integer existsByUsrMobNum(@Param("mobNum") String mobNum);
@Query("SELECT a.usrMobNum FROM AppUser a WHERE a.mobStatId.statId = 1")
List<String> findAllMobNums();
}

15
src/main/java/com/gcc_it_cell_services/springboot/repository/OtpLogRepository.java

@ -0,0 +1,15 @@
package com.gcc_it_cell_services.springboot.repository;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.gcc_it_cell_services.springboot.model.OtpLog;
@Repository
public interface OtpLogRepository extends JpaRepository<OtpLog, Integer> {
Optional<OtpLog> findTopByOtpReqMobNumOrderByDbReqTimestampDesc(String otpReqMobNum);
}

11
src/main/java/com/gcc_it_cell_services/springboot/repository/StatusRepository.java

@ -0,0 +1,11 @@
package com.gcc_it_cell_services.springboot.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.gcc_it_cell_services.springboot.model.Status;
@Repository
public interface StatusRepository extends JpaRepository<Status, Long> {
}

18
src/main/java/com/gcc_it_cell_services/springboot/repository/UrlCategoryRepository.java

@ -0,0 +1,18 @@
package com.gcc_it_cell_services.springboot.repository;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import com.gcc_it_cell_services.springboot.model.UrlCategory;
@Repository
public interface UrlCategoryRepository extends JpaRepository<UrlCategory, Long> {
@Query(nativeQuery = true, value = "SELECT * FROM tbl_url_cat_det WHERE url_cat_name = ?1")
Optional<UrlCategory> findUrlCatByUrlCatName(@Param("urlCatName") String urlCatName);
}

28
src/main/java/com/gcc_it_cell_services/springboot/repository/UrlDataRepository.java

@ -0,0 +1,28 @@
package com.gcc_it_cell_services.springboot.repository;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import com.gcc_it_cell_services.springboot.model.UrlData;
@Repository
public interface UrlDataRepository extends JpaRepository<UrlData, Long> {
@Query(value = "SELECT u FROM UrlData u ORDER BY LOWER(u.urlDataName)")
List<UrlData> findAllUrlsSortedByUrlName();
@Query(nativeQuery = true, value = """
SELECT * FROM tbl_url_data_det WHERE url_data_usr_name != '-' AND url_data_usr_pass != '-' ORDER BY LOWER(url_data_name) """)
List<UrlData> findAllUrlsWithUsrNameAndPass();
@Query("SELECT u FROM UrlData u WHERE u.urlCat.urlCatId = ?1 ORDER BY LOWER(u.urlDataName)")
List<UrlData> findAllUrlsByUrlCatId(@Param("urlCatId") Long urlCatId);
@Query("SELECT COUNT(u) FROM UrlData u WHERE u.urlCat.urlCatId = :urlCatId ORDER BY LOWER(u.urlDataName)")
Integer findUrlCountByUrlCatId(@Param("urlCatId") Long urlCatId);
}

22
src/main/java/com/gcc_it_cell_services/springboot/request_dto/AddUrlDataRequestDTO.java

@ -0,0 +1,22 @@
package com.gcc_it_cell_services.springboot.request_dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(doNotUseGetters = true, includeFieldNames = true)
public class AddUrlDataRequestDTO {
private String urlDataLink;
private String urlDataName;
private String urlDataUsrName;
private String urlDataUsrPass;
private String urlCatName;
}

17
src/main/java/com/gcc_it_cell_services/springboot/request_dto/AppUserRequestDTO.java

@ -0,0 +1,17 @@
package com.gcc_it_cell_services.springboot.request_dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString(doNotUseGetters = true, includeFieldNames = true)
@NoArgsConstructor
@AllArgsConstructor
public class AppUserRequestDTO {
private String usrName;
private String usrPass;
}

18
src/main/java/com/gcc_it_cell_services/springboot/request_dto/GetOtpRequestDTO.java

@ -0,0 +1,18 @@
package com.gcc_it_cell_services.springboot.request_dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString(doNotUseGetters = true, includeFieldNames = true)
@NoArgsConstructor
@AllArgsConstructor
public class GetOtpRequestDTO {
private String mobNumId; // changed from mobNum to mobNumId (UUID)
}

22
src/main/java/com/gcc_it_cell_services/springboot/request_dto/OtpLogRequestDTO.java

@ -0,0 +1,22 @@
package com.gcc_it_cell_services.springboot.request_dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString(includeFieldNames = true, doNotUseGetters = true)
@NoArgsConstructor
@AllArgsConstructor
public class OtpLogRequestDTO {
private Integer otp;
private String otpStat;
private String otpReqMobNum;
private String otpResMobNum;
private String otpLogMsg;
}

22
src/main/java/com/gcc_it_cell_services/springboot/request_dto/UpdateUrlDataRequestDTO.java

@ -0,0 +1,22 @@
package com.gcc_it_cell_services.springboot.request_dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString(doNotUseGetters = true, includeFieldNames = true)
@AllArgsConstructor
@NoArgsConstructor
public class UpdateUrlDataRequestDTO {
private Long urlDataId;
private String urlDataName;
private String urlDataLink;
private String urlDataUsrPass;
private String urlDataUsrName;
private String urlCatName;
private Long statId;
}

18
src/main/java/com/gcc_it_cell_services/springboot/request_dto/UrlCategoryRequestDTO.java

@ -0,0 +1,18 @@
package com.gcc_it_cell_services.springboot.request_dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString(doNotUseGetters = true, includeFieldNames = true)
@NoArgsConstructor
@AllArgsConstructor
public class UrlCategoryRequestDTO {
private String urlCatName;
}

26
src/main/java/com/gcc_it_cell_services/springboot/request_dto/UrlDataRequestDTO.java

@ -0,0 +1,26 @@
package com.gcc_it_cell_services.springboot.request_dto;
import com.gcc_it_cell_services.springboot.response_dto.StatusResponseDTO;
import com.gcc_it_cell_services.springboot.response_dto.UrlCategoryResponseDTO;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(doNotUseGetters = true, includeFieldNames = true)
public class UrlDataRequestDTO {
private String urlDataLink;
private String urlDataName;
private String urlDataUsrPass;
private String urlDataUsrName;
private UrlCategoryResponseDTO urlCatResDTO;
private StatusResponseDTO statResDTO;
}

19
src/main/java/com/gcc_it_cell_services/springboot/request_dto/VerifyOtpRequestDTO.java

@ -0,0 +1,19 @@
package com.gcc_it_cell_services.springboot.request_dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString(doNotUseGetters = true, includeFieldNames = true)
@AllArgsConstructor
@NoArgsConstructor
public class VerifyOtpRequestDTO {
private Integer otp;
private String mobNumId; // send the mobNumId from <select>
}

23
src/main/java/com/gcc_it_cell_services/springboot/response_dto/OtpLogResponseDTO.java

@ -0,0 +1,23 @@
package com.gcc_it_cell_services.springboot.response_dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString(doNotUseGetters = true, includeFieldNames = true)
@AllArgsConstructor
@NoArgsConstructor
public class OtpLogResponseDTO {
private Integer logId;
private Integer otp;
private String otpStat;
private String otpReqMobNum;
private String otpResMobNum;
private String otpLogMsg;
}

23
src/main/java/com/gcc_it_cell_services/springboot/response_dto/ServiceResponseDTO.java

@ -0,0 +1,23 @@
package com.gcc_it_cell_services.springboot.response_dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString(doNotUseGetters = true, includeFieldNames = true)
@AllArgsConstructor
@NoArgsConstructor
@JsonInclude(value = Include.NON_NULL)
public class ServiceResponseDTO {
private boolean resStat;
private String res;
}

23
src/main/java/com/gcc_it_cell_services/springboot/response_dto/StatusResponseDTO.java

@ -0,0 +1,23 @@
package com.gcc_it_cell_services.springboot.response_dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString(doNotUseGetters = true, includeFieldNames = true)
@AllArgsConstructor
@NoArgsConstructor
@JsonInclude(value = Include.NON_NULL)
public class StatusResponseDTO {
private Long statId;
private String statName;
}

19
src/main/java/com/gcc_it_cell_services/springboot/response_dto/UrlCategoryResponseDTO.java

@ -0,0 +1,19 @@
package com.gcc_it_cell_services.springboot.response_dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString(doNotUseGetters = true, includeFieldNames = true)
public class UrlCategoryResponseDTO {
private Long urlCatId;
private String urlCatName;
}

37
src/main/java/com/gcc_it_cell_services/springboot/response_dto/UrlDataResponseDTO.java

@ -0,0 +1,37 @@
package com.gcc_it_cell_services.springboot.response_dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString(doNotUseGetters = true, includeFieldNames = true)
@NoArgsConstructor
@AllArgsConstructor
public class UrlDataResponseDTO {
private Long urlDataId;
private String urlDataName;
private String urlDataLink;
private String urlDataUsrPass;
private String urlDataUsrName;
private UrlCategoryResponseDTO urlCatResDTO;
private String urlDataNum;
private StatusResponseDTO statResDTO;
public UrlDataResponseDTO(Long urlDataId, String urlDataName, String urlDataLink, String urlDataUsrPass,
String urlDataUsrName, UrlCategoryResponseDTO urlCatResDTO, StatusResponseDTO statResDTO) {
super();
this.urlDataId = urlDataId;
this.urlDataName = urlDataName;
this.urlDataLink = urlDataLink;
this.urlDataUsrPass = urlDataUsrPass;
this.urlDataUsrName = urlDataUsrName;
this.urlCatResDTO = urlCatResDTO;
this.statResDTO = statResDTO;
}
}

192
src/main/java/com/gcc_it_cell_services/springboot/rest_controller/HomeRestController.java

@ -0,0 +1,192 @@
package com.gcc_it_cell_services.springboot.rest_controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.gcc_it_cell_services.springboot.request_dto.AppUserRequestDTO;
import com.gcc_it_cell_services.springboot.request_dto.GetOtpRequestDTO;
import com.gcc_it_cell_services.springboot.request_dto.VerifyOtpRequestDTO;
import com.gcc_it_cell_services.springboot.response_dto.ServiceResponseDTO;
import com.gcc_it_cell_services.springboot.service.HomeService;
@RestController
@RequestMapping("/api")
public class HomeRestController {
@Autowired
private AuthenticationManager authManager;
@Autowired
private HomeService homeSer;
@Value("${app.getOtpUrl}")
private String otpUrl;
@PostMapping("/get-mob-num")
public ResponseEntity<List<Map<String, String>>> getAllMobNum() {
List<Map<String, String>> mobNums = homeSer.fetchAllMobNum();
if (!mobNums.isEmpty()) {
return ResponseEntity.ok(mobNums);
} else {
return ResponseEntity.noContent().build(); // 204 No Content
}
}
@PostMapping("/clr-otp-ses")
public void clrOtpSes() {
homeSer.clearMobNumTokenStore();
}
@PostMapping("/verify-otp")
public ResponseEntity<ServiceResponseDTO> verifyOtp(HttpServletRequest req,
@ModelAttribute VerifyOtpRequestDTO verifyOtpReqDTO) {
HttpSession ses = req.getSession(false);
ServiceResponseDTO serResDTO = homeSer.verifyOtp(verifyOtpReqDTO, ses);
if (serResDTO.isResStat()) {
// OTP verified successfully → cleanup session
ses.removeAttribute("otp");
ses.removeAttribute("otpTimestamp");
ses.removeAttribute("mobNum");
ses.removeAttribute("otpLogId");
return ResponseEntity.ok(serResDTO);
} else {
// OTP failed → remove old OTP only
ses.removeAttribute("otp");
return ResponseEntity.ok(serResDTO);
}
}
@PostMapping("/get-otp")
public ResponseEntity<Map<String, Object>> getOtp(HttpServletRequest req,
@ModelAttribute GetOtpRequestDTO getOtpReqDTO) {
Map<String, Object> data = homeSer.getOtp(getOtpReqDTO);
Integer otp = (Integer) data.remove("otp");
Integer otpLogId = (Integer) data.remove("otpLogId");
String mobNum = (String) data.remove("mobNum");
if (data.get("status").equals("success")) {
HttpSession ses = req.getSession();
ses.setAttribute("otp", otp);
ses.setAttribute("otpLogId", otpLogId);
ses.setAttribute("mobNum", mobNum);
ses.setAttribute("otpTimestamp", System.currentTimeMillis()); // <-- store timestamp
return ResponseEntity.ok(data);
} else {
String msg = (String) data.get("message");
switch (msg.replace("Failed to get OTP: ", "").trim()) {
case "Mobile number missing", "Invalid mobile number. It should be 10 digits.",
"Unknown mobile number is entered" -> {
return ResponseEntity.badRequest().body(data);
}
default -> {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(data);
}
}
}
}
@PostMapping("/val-app-usr-login")
public ResponseEntity<?> valUsrLogin(@ModelAttribute AppUserRequestDTO appUsrReqDTO, HttpServletRequest req) {
Map<String, Object> res = new HashMap<String, Object>();
try {
Authentication auth = authManager.authenticate(
new UsernamePasswordAuthenticationToken(appUsrReqDTO.getUsrName(), appUsrReqDTO.getUsrPass()));
/**
* Store authentication in session so Spring Security knows the user is logged
* in
**/
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(auth);
SecurityContextHolder.setContext(securityContext);
HttpSession httpSession = req.getSession(true);
httpSession.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, securityContext);
// Authentication successful
res.put("result", true);
res.put("usrName", auth.getName());
return ResponseEntity.ok(res);
} catch (AuthenticationException ae) {
Throwable cause = ae.getCause();
// 1️ Deactivated user (wrapped)
if (cause instanceof DisabledException) {
res.put("result", false);
res.put("error", "User is deactivated");
return ResponseEntity.status(403).body(res);
}
// 2️ Username not found (direct)
if (ae instanceof UsernameNotFoundException) {
res.put("result", false);
res.put("error", "User not found");
return ResponseEntity.status(404).body(res);
}
// 3️ Wrong password (direct)
if (ae instanceof BadCredentialsException) {
res.put("result", false);
res.put("error", "Invalid username or password");
return ResponseEntity.status(401).body(res);
}
// 4️ Default fallback
res.put("result", false);
res.put("error", "Authentication failed");
return ResponseEntity.status(401).body(res);
}
}
@PostMapping("/get-role")
public ResponseEntity<?> getUserRole(HttpServletRequest req) {
HttpSession session = req.getSession(false);
Map<String, Object> res = new HashMap<String, Object>();
if (session == null) {
res.put("authenticated", false);
res.put("isAdmin", false); // Not an admin if not authenticated
return ResponseEntity.ok(res);
}
SecurityContext sc = (SecurityContext) session
.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
if (sc == null || sc.getAuthentication() == null) {
res.put("authenticated", false);
res.put("isAdmin", false);
return ResponseEntity.ok(res);
}
Authentication auth = sc.getAuthentication();
boolean isAdmin = auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"));
res.put("authenticated", true);
res.put("isAdmin", isAdmin); // Send a simple boolean flag for isAdmin
return ResponseEntity.ok(res);
}
}

43
src/main/java/com/gcc_it_cell_services/springboot/rest_controller/UrlDataRestController.java

@ -0,0 +1,43 @@
package com.gcc_it_cell_services.springboot.rest_controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.gcc_it_cell_services.springboot.request_dto.AddUrlDataRequestDTO;
import com.gcc_it_cell_services.springboot.request_dto.UpdateUrlDataRequestDTO;
import com.gcc_it_cell_services.springboot.response_dto.ServiceResponseDTO;
import com.gcc_it_cell_services.springboot.service.UrlDataService;
@RestController
@RequestMapping("/api/urls")
public class UrlDataRestController {
@Autowired
private UrlDataService urlSer;
@PostMapping("/add-url-data")
public ResponseEntity<ServiceResponseDTO> addFile(@ModelAttribute AddUrlDataRequestDTO addUrlDataReqDTO) {
ServiceResponseDTO serResDTO = urlSer.addUrl(addUrlDataReqDTO);
if (serResDTO.isResStat()) {
return ResponseEntity.ok(serResDTO);
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(serResDTO);
}
}
@PostMapping("/upd-url-data")
public ResponseEntity<ServiceResponseDTO> updFile(@ModelAttribute UpdateUrlDataRequestDTO updUrlDataReqDTO) {
ServiceResponseDTO serResDTO = urlSer.updUrl(updUrlDataReqDTO);
if (serResDTO.isResStat()) {
return ResponseEntity.ok(serResDTO);
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(serResDTO);
}
}
}

279
src/main/java/com/gcc_it_cell_services/springboot/service/HomeService.java

@ -0,0 +1,279 @@
package com.gcc_it_cell_services.springboot.service;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;
import com.gcc_it_cell_services.springboot.mapper.OtpLogMapper;
import com.gcc_it_cell_services.springboot.model.AppUser;
import com.gcc_it_cell_services.springboot.repository.AppUserRepository;
import com.gcc_it_cell_services.springboot.repository.OtpLogRepository;
import com.gcc_it_cell_services.springboot.request_dto.GetOtpRequestDTO;
import com.gcc_it_cell_services.springboot.request_dto.OtpLogRequestDTO;
import com.gcc_it_cell_services.springboot.request_dto.VerifyOtpRequestDTO;
import com.gcc_it_cell_services.springboot.response_dto.OtpLogResponseDTO;
import com.gcc_it_cell_services.springboot.response_dto.ServiceResponseDTO;
import com.gcc_it_cell_services.springboot.util.MobileNumberTokenStore;
@Service
public class HomeService implements UserDetailsService {
@Autowired
private AppUserRepository appUsrRepo;
@Autowired
private OtpLogRepository otpLogRepo;
@Autowired
private OtpLogMapper otpLogMap;
@Value("${app.getOtpUrl}")
private String otpUrl;
@Autowired
private MobileNumberTokenStore mobNumTokenStore;
private static final Logger logger = LoggerFactory.getLogger(HomeService.class);
// Helper method
private String maskMobNum(String mobNum) {
if (mobNum == null || mobNum.length() < 4) {
throw new RuntimeException("Invalid Mobile Number: " + mobNum);
}
String start = mobNum.substring(0, 2);
String end = mobNum.substring(mobNum.length() - 4);
return start + "****" + end;
}
@Transactional(readOnly = true)
public List<Map<String, String>> fetchAllMobNum() {
List<Map<String, String>> res = new ArrayList<Map<String, String>>();
try {
// Fetch the list of mobile numbers
List<String> mobNums = appUsrRepo.findAllMobNums();
// Iterate through each mobile number and generate a masked version
for (String mobNum : mobNums) {
String uuid = UUID.randomUUID().toString();
mobNumTokenStore.storeMobNum(uuid, mobNum); // Store UUID and real mobile number mapping
Map<String, String> maskMobNum = new HashMap<String, String>();
maskMobNum.put("mobNumId", uuid); // Send the UUID to frontend
maskMobNum.put("mobNum", maskMobNum(mobNum)); // Masking the mobile number
res.add(maskMobNum);
}
} catch (RuntimeException re) {
re.printStackTrace();
logger.error("Exception occurred while fetching mobile numbers: {}", re.getMessage(), re);
} catch (Exception e) {
e.printStackTrace();
logger.error("Exception occurred while fetching mobile numbers: {}", e.getMessage(), e);
}
return res;
}
@Override
public UserDetails loadUserByUsername(String usrname) throws UsernameNotFoundException {
// TODO Auto-generated method stub
logger.info("Loading user: {}", usrname);
AppUser appUsr = appUsrRepo.findByUsrName(usrname)
.orElseThrow(() -> new UsernameNotFoundException("User not found with username: " + usrname));
if (appUsr.getUsrStatId().getStatId().equals(2L)) {
throw new DisabledException("User is deactivated");
}
// Bcrypt for password verification
UserDetails usrDets = User.withUsername(appUsr.getUsrName()).password(appUsr.getUsrPass())
.authorities(appUsr.getUsrRole().getUsrRoleName()).build();
return usrDets;
}
@Transactional(rollbackFor = Exception.class)
public Map<String, Object> getOtp(GetOtpRequestDTO getOtpReqDTO) {
// TODO Auto-generated method stub
Map<String, Object> mapData = new HashMap<String, Object>();
// Lookup real mobile number from UUID
String mobNum = mobNumTokenStore.getMobNum(getOtpReqDTO.getMobNumId());
try {
if (mobNum == null || mobNum.isBlank()) {
throw new RuntimeException("Mobile number missing");
}
if (!mobNum.matches("\\d{10}")) {
throw new RuntimeException("Invalid mobile number. It should be 10 digits.");
}
if (appUsrRepo.existsByUsrMobNum(mobNum) == 0) {
throw new RuntimeException("Unknown mobile number is entered");
}
Integer otp = new SecureRandom().nextInt(90000) + 10000;
// Construct the message using new template
// Template: "Welcome to GCC! Your OTP for {#var#} is {#var#}."
/* String appName = "GCC Swimming Pool booking"; */
String appName = "GCC IT Cell Services";
String generatedMessage = "Welcome to GCC! Your OTP for " + appName + " is " + otp + ".";
// SMS API parameters
String key = "pfTEYN6H";
String entityId = "1401572690000011081";
String templateId = "1407175765770900925";
// Construct full API URL
String smsUrl = otpUrl + "?from=GCCCRP" + "&key=" + key + "&sender=GCCCRP" + "&to=" + mobNum + "&body="
+ generatedMessage + "&entityid=" + entityId + "&templateid=" + templateId;
System.out.println("urlString: " + smsUrl);
String smsRes = new RestTemplate().getForObject(smsUrl, String.class);
logger.info("SMS API response: {}", smsRes);
logger.info("OTP sent successfully to {} with {} ", mobNum, otp);
otpLogRepo.save(otpLogMap.toOtpLogEnt(new OtpLogRequestDTO(otp, "STATE 1(A): OTP REQUEST SUCCESS", mobNum,
"-", "OTP sent successfully to " + mobNum + " with " + otp + ". (OTP request phase)")));
OtpLogResponseDTO otpLogResDTO = otpLogMap
.toOtpLogResDTO(otpLogRepo.findTopByOtpReqMobNumOrderByDbReqTimestampDesc(mobNum).get());
mapData.put("status", "success");
mapData.put("message", "OTP sent successfully");
mapData.put("otp", otp);
mapData.put("mobNum", mobNum);
mapData.put("otpLogId", otpLogResDTO.getLogId());
} catch (Exception e) {
logger.error("Exception occurred while generating OTP: {}", e.getMessage(), e);
e.printStackTrace();
mapData.put("status", "error");
mapData.put("message", "Failed to get OTP: " + e.getMessage());
otpLogRepo.save(otpLogMap.toOtpLogEnt(
new OtpLogRequestDTO(0, "STATE 1(B): OTP REQUEST FAILED", mobNum, "-", e.getMessage())));
}
return mapData;
}
@Transactional(rollbackFor = Exception.class)
public ServiceResponseDTO verifyOtp(VerifyOtpRequestDTO verifyOtpReqDTO, HttpSession ses) {
// Lookup real mobile number from mobNumId
String mobNum = mobNumTokenStore.getMobNum(verifyOtpReqDTO.getMobNumId());
Integer savedOtp = (Integer) ses.getAttribute("otp");
String savedMobNum = (String) ses.getAttribute("mobNum");
Integer otpLogId = (Integer) ses.getAttribute("otpLogId");
try {
if (ses == null || ses.getAttribute("otp") == null || ses.getAttribute("otpTimestamp") == null) {
throw new IllegalArgumentException("Session expired or OTP not found. Please request a new OTP.");
}
// === TTL Check (e.g., 2 minutes) ===
Long otpTimestamp = (Long) ses.getAttribute("otpTimestamp");
if (otpTimestamp == null || (System.currentTimeMillis() - otpTimestamp) > 120000) {
ses.removeAttribute("otp");
ses.removeAttribute("otpTimestamp");
throw new IllegalArgumentException("OTP expired. Please request a new one.");
}
// === Fetch Existing Log ===
OtpLogResponseDTO otpLogResDTO = otpLogMap.toOtpLogResDTO(otpLogRepo.findById(otpLogId)
.orElseThrow(() -> new RuntimeException("Request OTP log record not found.")));
if (mobNum == null) {
throw new IllegalArgumentException("Invalid mobile number");
}
// === Validation ===
if (!mobNum.equals(savedMobNum)) {
throw new IllegalArgumentException("Mobile number mismatched.");
} else if (!verifyOtpReqDTO.getOtp().equals(savedOtp)) {
throw new IllegalArgumentException("Invalid OTP");
}
// === SUCCESS CASE ===
otpLogResDTO.setOtpResMobNum(mobNum);
otpLogResDTO.setOtpLogMsg(
"OTP verified successfully from mobile number " + mobNum + ". (OTP verification phase)");
otpLogResDTO.setOtpStat("STATE 2(A): OTP VERIFICATION SUCCESS");
otpLogRepo.save(otpLogMap.toOtpLogEnt(otpLogResDTO));
// Clear ALL UUID Mobile mappings after successful verification
mobNumTokenStore.clearAll();
logger.info("Mobile Number Token Store Cleared");
logger.info("OTP verified successfully");
return new ServiceResponseDTO(true, "OTP verified successfully");
} catch (IllegalArgumentException iae) {
logger.error("Illegal Argument Exception occurred while verifying OTP: {}", iae.getMessage(), iae);
OtpLogResponseDTO otpLogResDTO = otpLogMap.toOtpLogResDTO(otpLogRepo.findById(otpLogId).orElseGet(
() -> otpLogRepo.findTopByOtpReqMobNumOrderByDbReqTimestampDesc(savedMobNum).orElse(null)));
if (otpLogResDTO != null) {
otpLogResDTO.setOtpResMobNum(mobNum);
otpLogResDTO.setOtpLogMsg("OTP verification failed for mobile number " + mobNum
+ ". (OTP verification phase). Reason: " + iae.getMessage());
otpLogResDTO.setOtpStat("OTP expired. Please request a new one.".equalsIgnoreCase(iae.getMessage())
? "STATE 2(B): OTP EXPIRED"
: "STATE 2(C): OTP VERIFICATION FAILED");
otpLogRepo.save(otpLogMap.toOtpLogEnt(otpLogResDTO));
}
logger.info("Mobile Number Token Store Cleared");
mobNumTokenStore.clearAll(); // CLEAR MAP ON FAILURE
return new ServiceResponseDTO(false, "Error: " + iae.getMessage());
} catch (RuntimeException re) {
logger.error("Runtime Exception occurred while verifying OTP: {}", re.getMessage(), re);
OtpLogResponseDTO otpLogResDTO = otpLogMap.toOtpLogResDTO(otpLogRepo.findById(otpLogId).orElseGet(
() -> otpLogRepo.findTopByOtpReqMobNumOrderByDbReqTimestampDesc(savedMobNum).orElse(null)));
if (otpLogResDTO != null) {
otpLogResDTO.setOtpResMobNum(mobNum);
otpLogResDTO.setOtpLogMsg("OTP verification failed for mobile number " + mobNum
+ ". (OTP verification phase). Reason: " + re.getMessage());
otpLogResDTO.setOtpStat("STATE 2(C): OTP VERIFICATION FAILED");
otpLogRepo.save(otpLogMap.toOtpLogEnt(otpLogResDTO));
}
logger.info("Mobile Number Token Store Cleared");
mobNumTokenStore.clearAll(); // CLEAR MAP ON FAILURE
return new ServiceResponseDTO(false, "Error: " + re.getMessage());
} catch (Exception e) {
logger.error("Exception occurred while verifying OTP: {}", e.getMessage(), e);
OtpLogResponseDTO otpLogResDTO = otpLogMap.toOtpLogResDTO(otpLogRepo.findById(otpLogId).orElseGet(
() -> otpLogRepo.findTopByOtpReqMobNumOrderByDbReqTimestampDesc(savedMobNum).orElse(null)));
if (otpLogResDTO != null) {
otpLogResDTO.setOtpResMobNum(mobNum);
otpLogResDTO.setOtpLogMsg("OTP verification failed for mobile number " + mobNum
+ ". (OTP verification phase). Reason: " + e.getMessage());
otpLogResDTO.setOtpStat("STATE 2(C): OTP VERIFICATION FAILED");
otpLogRepo.save(otpLogMap.toOtpLogEnt(otpLogResDTO));
}
logger.info("Mobile Number Token Store Cleared");
mobNumTokenStore.clearAll(); // CLEAR MAP ON FAILURE
return new ServiceResponseDTO(false, "Error: " + e.getMessage());
}
}
public void clearMobNumTokenStore() {
// TODO Auto-generated method stub
logger.info("Mobile Number Token Store Cleared");
mobNumTokenStore.clearAll();
}
}

174
src/main/java/com/gcc_it_cell_services/springboot/service/UrlDataCacheService.java

@ -0,0 +1,174 @@
package com.gcc_it_cell_services.springboot.service;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.gcc_it_cell_services.springboot.mapper.UrlCategoryMapper;
import com.gcc_it_cell_services.springboot.mapper.UrlDataMapper;
import com.gcc_it_cell_services.springboot.repository.UrlCategoryRepository;
import com.gcc_it_cell_services.springboot.repository.UrlDataRepository;
import com.gcc_it_cell_services.springboot.response_dto.UrlCategoryResponseDTO;
import com.gcc_it_cell_services.springboot.response_dto.UrlDataResponseDTO;
@Service
public class UrlDataCacheService {
@Autowired
private UrlCategoryRepository urlCatRepo;
@Autowired
private UrlDataRepository urlDataRepo;
@Autowired
private UrlDataMapper urlDataMap;
@Autowired
private UrlCategoryMapper urlCatMap;
private final Map<String, String> globalUrlData = new ConcurrentHashMap<String, String>();
private final Map<String, Map<String, String>> globalUrlCatData = new ConcurrentHashMap<String, Map<String, String>>();
private final Map<String, String> globalUrlLinks = new ConcurrentHashMap<String, String>();
// Called from CacheInitializer
@Transactional(readOnly = true)
public synchronized void loadCache() {
buildCache();
}
// Clear and rebuild cache
@Transactional(readOnly = true)
public synchronized void refreshCache() {
buildCache();
}
// Getters used by other service
public Map<String, String> getGlobalUrlData() {
return globalUrlData;
}
public Map<String, Map<String, String>> getGlobalUrlCatData() {
return globalUrlCatData;
}
public Map<String, String> getGlobalUrlLinks() {
return globalUrlLinks;
}
private boolean isAct(UrlDataResponseDTO dto) {
return dto.getStatResDTO() != null && dto.getStatResDTO().getStatId() != null
&& dto.getStatResDTO().getStatId() == 1L;
}
private void buildCache() {
// TODO Auto-generated method stub
globalUrlData.clear();
globalUrlCatData.clear();
globalUrlLinks.clear();
List<UrlCategoryResponseDTO> urlCatResDTOs = urlCatRepo.findAll().stream().map(urlCatMap::toUrlCatResDTO)
.toList();
List<UrlDataResponseDTO> urlDataResDTOs = urlDataRepo.findAll().stream().map(urlDataMap::toUrlDataResDTO)
.filter(this::isAct).toList();
// Group URLS by category to avoid repeated filtering
Map<String, List<UrlDataResponseDTO>> urlsByCat = urlDataResDTOs.stream()
.collect(Collectors.groupingBy(urlDataResDTO -> urlDataResDTO.getUrlCatResDTO().getUrlCatName()));
// Build numbering list
List<UrlDataResponseDTO> finalUrlDataResDTOs = new ArrayList<UrlDataResponseDTO>();
for (UrlCategoryResponseDTO urlCatResDTO : urlCatResDTOs) {
String catName = urlCatResDTO.getUrlCatName();
if (catName.equalsIgnoreCase("-")) {
urlsByCat.getOrDefault("-", List.of()).forEach(urlDataResDTO -> {
finalUrlDataResDTOs.add(new UrlDataResponseDTO(null, urlDataResDTO.getUrlDataName(),
urlDataResDTO.getUrlDataLink(), null, null, null, null));
});
} else {
if (urlsByCat.containsKey(catName)) {
// Check: This category has at least one ACTIVE url
UrlDataResponseDTO urlDataResDTO = new UrlDataResponseDTO();
urlDataResDTO.setUrlDataName(catName);
urlDataResDTO.setUrlDataLink("/urls/get-" + catName.replace(" ", "-") + "-urls");
finalUrlDataResDTOs.add(urlDataResDTO);
}
}
}
// Sort main list
finalUrlDataResDTOs.sort((urlDataResDTO1, urlDataResDTO2) -> {
String urlDataName1 = urlDataResDTO1.getUrlDataName().toLowerCase();
String urlDataName2 = urlDataResDTO2.getUrlDataName().toLowerCase();
boolean isNum1 = Character.isDigit(urlDataName1.charAt(0));
boolean isNum2 = Character.isDigit(urlDataName2.charAt(0));
if (isNum1 && !isNum2) {
return -1;
}
if (isNum1 && isNum2) {
return 1;
}
return urlDataName1.compareTo(urlDataName2);
});
// Assign main numbering
Integer count = 1;
for (UrlDataResponseDTO urlDataResDTO : finalUrlDataResDTOs) {
String mainNum = String.valueOf(count++);
// IMPORTANT: exact-case key
String nameKey = urlDataResDTO.getUrlDataName();
globalUrlData.put(nameKey, mainNum);
globalUrlLinks.put(nameKey, urlDataResDTO.getUrlDataLink());
}
// Per category numbering
for (UrlCategoryResponseDTO urlCatResDTO : urlCatResDTOs) {
String catName = urlCatResDTO.getUrlCatName();
// Skip "-" category
if (catName.equalsIgnoreCase("-")) {
continue;
}
// Get URLs under this category from memory (NOT DB)
List<UrlDataResponseDTO> urlDataResDTOsByUrlCat = urlsByCat.getOrDefault(catName, List.of());
// Skip empty category
if (urlDataResDTOsByUrlCat.isEmpty()) {
continue;
}
// Sort alpahbetically
urlDataResDTOsByUrlCat = urlDataResDTOsByUrlCat.stream()
.sorted(Comparator.comparing(UrlDataResponseDTO::getUrlDataName, String.CASE_INSENSITIVE_ORDER))
.toList();
// Prepare per-category numbering
Map<String, String> perCatMap = new HashMap<String, String>();
// exact category name
String UrlDataNum = globalUrlData.get(catName);
Integer alphaCount = 0;
for (UrlDataResponseDTO urlDataResDTO : urlDataResDTOsByUrlCat) {
char alpha = (char) ('A' + alphaCount++);
String catNum = UrlDataNum + alpha;
// IMPORTANT: exact case
perCatMap.put(urlDataResDTO.getUrlDataName(), catNum);
}
// Store this map under the category name
globalUrlCatData.put(catName, perCatMap);
}
}
}

280
src/main/java/com/gcc_it_cell_services/springboot/service/UrlDataService.java

@ -0,0 +1,280 @@
package com.gcc_it_cell_services.springboot.service;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.gcc_it_cell_services.springboot.mapper.StatusMapper;
import com.gcc_it_cell_services.springboot.mapper.UrlCategoryMapper;
import com.gcc_it_cell_services.springboot.mapper.UrlDataMapper;
import com.gcc_it_cell_services.springboot.model.UrlData;
import com.gcc_it_cell_services.springboot.repository.StatusRepository;
import com.gcc_it_cell_services.springboot.repository.UrlCategoryRepository;
import com.gcc_it_cell_services.springboot.repository.UrlDataRepository;
import com.gcc_it_cell_services.springboot.request_dto.AddUrlDataRequestDTO;
import com.gcc_it_cell_services.springboot.request_dto.UpdateUrlDataRequestDTO;
import com.gcc_it_cell_services.springboot.request_dto.UrlCategoryRequestDTO;
import com.gcc_it_cell_services.springboot.request_dto.UrlDataRequestDTO;
import com.gcc_it_cell_services.springboot.response_dto.ServiceResponseDTO;
import com.gcc_it_cell_services.springboot.response_dto.StatusResponseDTO;
import com.gcc_it_cell_services.springboot.response_dto.UrlCategoryResponseDTO;
import com.gcc_it_cell_services.springboot.response_dto.UrlDataResponseDTO;
@Service
public class UrlDataService {
@Autowired
private UrlDataRepository urlDataRepo;
@Autowired
private UrlCategoryMapper urlCatMap;
@Autowired
private UrlCategoryRepository urlCatRepo;
@Autowired
private UrlDataMapper urlDataMap;
@Autowired
private StatusMapper statMap;
@Autowired
private StatusRepository statRepo;
@Autowired
private UrlDataCacheService urlDataCacheSer;
private static final Logger logger = LoggerFactory.getLogger(UrlDataService.class);
@Transactional(readOnly = true)
public List<StatusResponseDTO> getAllStats() {
return statRepo.findAll().stream().map(statMap::toStatResDTO).toList();
}
@Transactional(readOnly = true)
public List<UrlDataResponseDTO> getAllUrlsSortedByUrlName() {
// TODO Auto-generated method stub
return urlDataRepo.findAllUrlsSortedByUrlName().stream().map(urlData -> urlDataMap.toUrlDataResDTO(urlData))
.collect(Collectors.toList());
}
@Transactional(readOnly = true)
public List<UrlDataResponseDTO> getAllUrls() {
// TODO Auto-generated method stub
Map<String, String> globalUrlData = urlDataCacheSer.getGlobalUrlData();
Map<String, String> globalUrlLinks = urlDataCacheSer.getGlobalUrlLinks();
List<UrlDataResponseDTO> urlDataResDTOs = new ArrayList<UrlDataResponseDTO>();
// Build final list from main numbering map
for (Map.Entry<String, String> entry : globalUrlData.entrySet()) {
String nameKey = entry.getKey();
String mainNum = entry.getValue();
String link = globalUrlLinks.getOrDefault(nameKey, null);
UrlDataResponseDTO urlDataResDTO = new UrlDataResponseDTO();
urlDataResDTO.setUrlDataName(nameKey);
urlDataResDTO.setUrlDataNum(mainNum);
urlDataResDTO.setUrlDataLink(link);
urlDataResDTOs.add(urlDataResDTO);
}
// Sort by numbering
urlDataResDTOs.sort(Comparator.comparing(UrlDataResponseDTO::getUrlDataName, String.CASE_INSENSITIVE_ORDER));
return urlDataResDTOs;
}
@Transactional(readOnly = true)
public List<UrlDataResponseDTO> getAllUrlsWithUsrNameAndPass() {
// TODO Auto-generated method stub
List<UrlDataResponseDTO> urlDataResDTOs = urlDataRepo.findAllUrlsWithUsrNameAndPass().stream()
.map(urlDataMap::toUrlDataResDTO)
.filter(urlDataResDTO -> urlDataResDTO.getStatResDTO() != null
&& urlDataResDTO.getStatResDTO().getStatId().equals(1L))
.sorted(Comparator.comparing(UrlDataResponseDTO::getUrlDataName, String.CASE_INSENSITIVE_ORDER))
.collect(Collectors.toList());
// Assign numbering using cache
for (UrlDataResponseDTO urlDataResDTO : urlDataResDTOs) {
String urlName = urlDataResDTO.getUrlDataName(); // Always exists
String catName = urlDataResDTO.getUrlCatResDTO().getUrlCatName();
// Always set MAIN number using URL NAME
String mainNum = urlDataCacheSer.getGlobalUrlData().get(urlName);
urlDataResDTO.setUrlDataNum(mainNum);
// Skip alphabetic numbering for uncategorized
if (catName == null || catName.equals("-")) {
continue; // main numbering already applied
}
// Apply subcategory numbering if available
Map<String, String> perCatMap = urlDataCacheSer.getGlobalUrlCatData().get(catName);
if (perCatMap != null) {
String subNum = perCatMap.get(urlName);
if (subNum != null) {
urlDataResDTO.setUrlDataNum(subNum); // override main numbering
}
}
}
// FINAL SORT BASED ON ASSIGNED NUMBER
urlDataResDTOs.sort((a, b) -> {
String num1 = a.getUrlDataNum();
String num2 = b.getUrlDataNum();
if (num1 == null)
return 1;
if (num2 == null)
return -1;
// Extract numeric + alpha suffix
var m1 = num1.matches("(\\d+)([A-Za-z]*)") ? num1.replaceAll("(\\d+)([A-Za-z]*)", "$1,$2").split(",")
: new String[] { "999999", "" };
var m2 = num2.matches("(\\d+)([A-Za-z]*)") ? num2.replaceAll("(\\d+)([A-Za-z]*)", "$1,$2").split(",")
: new String[] { "999999", "" };
int n1 = Integer.parseInt(m1[0]);
int n2 = Integer.parseInt(m2[0]);
// Compare numeric
if (n1 != n2)
return Integer.compare(n1, n2);
// Compare alphabetic suffix (empty suffix should come first)
String s1 = m1.length > 1 ? m1[1] : "";
String s2 = m2.length > 1 ? m2[1] : "";
if (s1.isEmpty() && !s2.isEmpty())
return -1;
if (!s1.isEmpty() && s2.isEmpty())
return 1;
return s1.compareToIgnoreCase(s2);
});
return urlDataResDTOs;
}
@Transactional(rollbackFor = Exception.class)
public ServiceResponseDTO addUrl(AddUrlDataRequestDTO addUrlDataReqDTO) {
// TODO Auto-generated method stub
try {
String urlLink = addUrlDataReqDTO.getUrlDataLink().trim();
String urlName = addUrlDataReqDTO.getUrlDataName().trim();
String reqUrlUsrName = addUrlDataReqDTO.getUrlDataUsrName().trim();
String reqUrlUsrPass = addUrlDataReqDTO.getUrlDataUsrPass().trim();
String reqUrlCatName = addUrlDataReqDTO.getUrlCatName().trim();
String urlUsrName = reqUrlUsrName.isEmpty() ? "-" : reqUrlUsrName;
String urlUsrPass = reqUrlUsrPass.isEmpty() ? "-" : reqUrlUsrPass;
String urlCatName = reqUrlCatName.isEmpty() ? "-" : reqUrlCatName;
UrlCategoryResponseDTO urlCatResDTO = urlCatMap
.toUrlCatResDTO(urlCatRepo.findUrlCatByUrlCatName(urlCatName).orElseGet(() -> {
UrlCategoryRequestDTO urlCatReqDTO = new UrlCategoryRequestDTO(urlCatName);
return urlCatRepo.save(urlCatMap.toUrlCatEnt(urlCatReqDTO));
}));
StatusResponseDTO statResDTO = statMap.toStatResDTO(statRepo.findById(1L).get());
urlDataRepo.save(urlDataMap.toUrlDatEnt(
new UrlDataRequestDTO(urlLink, urlName, urlUsrPass, urlUsrName, urlCatResDTO, statResDTO)));
// Refresh the cache after adding
urlDataCacheSer.refreshCache();
logger.info("URL added successfully");
return new ServiceResponseDTO(true, "URL added successfully");
} catch (Exception e) {
logger.error("Exception occurred while adding url: {}", e.getMessage(), e);
e.printStackTrace();
return new ServiceResponseDTO(false, "Failed to add the URL. Error: " + e.getMessage());
}
}
@Transactional(rollbackFor = Exception.class)
public ServiceResponseDTO updUrl(UpdateUrlDataRequestDTO updUrlDataReqDTO) {
// TODO Auto-generated method stub
try {
UrlData urlData = urlDataRepo.findById(updUrlDataReqDTO.getUrlDataId())
.orElseThrow(() -> new RuntimeException("URL not found"));
UrlDataResponseDTO urlDataResDTO = urlDataMap.toUrlDataResDTO(urlData);
String reqUrlUsrName = updUrlDataReqDTO.getUrlDataUsrName().trim();
String reqUrlUsrPass = updUrlDataReqDTO.getUrlDataUsrPass().trim();
String reqUrlCatName = updUrlDataReqDTO.getUrlCatName().trim();
String urlUsrName = reqUrlUsrName.isEmpty() ? "-" : reqUrlUsrName;
String urlUsrPass = reqUrlUsrPass.isEmpty() ? "-" : reqUrlUsrPass;
String urlCatName = reqUrlCatName.isEmpty() ? "-" : reqUrlCatName;
UrlCategoryResponseDTO urlCatResDTO = urlCatMap
.toUrlCatResDTO(urlCatRepo.findUrlCatByUrlCatName(urlCatName).orElseGet(() -> {
UrlCategoryRequestDTO urlCatReqDTO = new UrlCategoryRequestDTO(urlCatName);
return urlCatRepo.save(urlCatMap.toUrlCatEnt(urlCatReqDTO));
}));
StatusResponseDTO statResDTO = statMap.toStatResDTO(statRepo.findById(updUrlDataReqDTO.getStatId()).get());
urlDataResDTO.setUrlDataId(updUrlDataReqDTO.getUrlDataId());
urlDataResDTO.setUrlDataLink(updUrlDataReqDTO.getUrlDataLink());
urlDataResDTO.setUrlDataName(updUrlDataReqDTO.getUrlDataName());
urlDataResDTO.setUrlDataUsrName(urlUsrName);
urlDataResDTO.setUrlDataUsrPass(urlUsrPass);
urlDataResDTO.setUrlCatResDTO(urlCatResDTO);
urlDataResDTO.setStatResDTO(statResDTO);
urlDataRepo.save(urlDataMap.toUrlDatEnt(urlDataResDTO));
// Refresh the cache after adding
urlDataCacheSer.refreshCache();
logger.info("URL updated successfully");
return new ServiceResponseDTO(true, "URL updated successfully");
} catch (Exception e) {
logger.error("Exception occurred while updating url: {}", e.getMessage(), e);
e.printStackTrace();
return new ServiceResponseDTO(false, "Failed to update the URL. Error: " + e.getMessage());
}
}
@Transactional(readOnly = true)
public List<UrlDataResponseDTO> getUrlsByUrlCatName(String urlCatName) {
// TODO Auto-generated method stub
// Fetch Category ID
Long urlCatId = urlCatRepo.findUrlCatByUrlCatName(urlCatName).map(urlCatMap::toUrlCatResDTO).get()
.getUrlCatId();
// Fetch URLs under category
List<UrlDataResponseDTO> urls = urlDataRepo.findAllUrlsByUrlCatId(urlCatId).stream()
.map(urlDataMap::toUrlDataResDTO)
.filter(urlDataResDTO -> urlDataResDTO.getStatResDTO() != null
&& urlDataResDTO.getStatResDTO().getStatId().equals(1L))
.sorted(Comparator.comparing(UrlDataResponseDTO::getUrlDataName, String.CASE_INSENSITIVE_ORDER))
.toList();
// Read per-category numbering map using category key (lowercase)
Map<String, String> perCatMap = urlDataCacheSer.getGlobalUrlCatData().get(urlCatName);
// Assign numbering
if (perCatMap != null) {
urls.forEach(dto -> {
// EXACT NAME MATCH
String num = perCatMap.get(dto.getUrlDataName());
dto.setUrlDataNum(num);
});
}
return urls;
}
}

28
src/main/java/com/gcc_it_cell_services/springboot/util/MobileNumberTokenStore.java

@ -0,0 +1,28 @@
package com.gcc_it_cell_services.springboot.util;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.stereotype.Component;
@Component
public class MobileNumberTokenStore {
private final Map<String, String> tokenToMobNum = new ConcurrentHashMap<String, String>();
// Store all mobile number with UUID tokens when all mobile numbers are fetched
public void storeMobNum(String token, String mobNum) {
tokenToMobNum.put(token, mobNum);
}
// Retrieve mobile number using UUID token
public String getMobNum(String token) {
return tokenToMobNum.get(token);
}
// Remove all UUID tokens (after OTP verification)
public void clearAll() {
tokenToMobNum.clear();
}
}

21
src/main/java/com/gcc_it_cell_services/springboot/util/UrlCacheScheduler.java

@ -0,0 +1,21 @@
package com.gcc_it_cell_services.springboot.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import com.gcc_it_cell_services.springboot.service.UrlDataCacheService;
@Component
public class UrlCacheScheduler {
@Autowired
private UrlDataCacheService urlDataCacheSer;
// Runs every 1 minute
@Scheduled(fixedRate = 60 * 60 * 1000) // 60 minutes * 60 seconds * 1000 ms = 1 hour
public void refreshUrlCachePeriodically() {
urlDataCacheSer.refreshCache(); // full rebuild
}
}

22
src/main/java/com/gcc_it_cell_services/springboot/util/UrlDataCacheInitializer.java

@ -0,0 +1,22 @@
package com.gcc_it_cell_services.springboot.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import com.gcc_it_cell_services.springboot.service.UrlDataCacheService;
@Component
public class UrlDataCacheInitializer implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
private UrlDataCacheService urlDataCacheSer;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// TODO Auto-generated method stub
urlDataCacheSer.loadCache();
}
}

9
src/main/resources/META-INF/additional-spring-configuration-metadata.json

@ -0,0 +1,9 @@
{
"properties": [
{
"name": "app.getOtpUrl",
"type": "java.lang.String",
"description": "A description for 'app.getOtpUrl'"
}
]
}

33
src/main/resources/application-local.properties

@ -0,0 +1,33 @@
spring.application.name=gcc-it-cell-services
server.port=8051
server.address=0.0.0.0
# spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
# ===============================
# MySQL Database Configuration
# ===============================
# Local DB server
spring.datasource.url=jdbc:mysql://localhost:3306/gcc-it-cell-services
spring.datasource.username=root
spring.datasource.password=1916008_Saec
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# spring.datasource.url=jdbc:oracle:thin:@10.1.0.14:1521:chncorp
# spring.datasource.username=fileadjust
# spring.datasource.password=fileadjust
# spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
# ===============================
# JPA / Hibernate Config
# ===============================
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
# spring.jpa.properties.hibernate.format_sql=true
spring.jpa.open-in-view=true
logging.level.org.hibernate.SQL=DEBUG
# logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
logging.level.org.hibernate.type.descriptor.sql=TRACE
# Production context path
server.servlet.context-path=/

29
src/main/resources/application-prod.properties

@ -0,0 +1,29 @@
spring.application.name=gcc-it-cell-services
server.port=8051
server.address=0.0.0.0
# spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
# ===============================
# MySQL Database Configuration
# ===============================
# Local DB server
spring.datasource.url=jdbc:mysql://localhost:3306/gcc-it-cell-services
spring.datasource.username=root
spring.datasource.password=1916008_Saec
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# ===============================
# JPA / Hibernate Config
# ===============================
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
# spring.jpa.properties.hibernate.format_sql=true
spring.jpa.open-in-view=true
logging.level.org.hibernate.SQL=DEBUG
# logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
logging.level.org.hibernate.type.descriptor.sql=TRACE
# Production context path
server.servlet.context-path=/itcell

2
src/main/resources/application.properties

@ -0,0 +1,2 @@
spring.profiles.active=prod
app.getOtpUrl=https://tmegov.onex-aura.com/api/sms

11794
src/main/resources/static/assets/bootstrap-5.3.2/css/bootstrap.min.css
File diff suppressed because it is too large
View File

2212
src/main/resources/static/assets/bootstrap-5.3.2/js/bootstrap.min.js
File diff suppressed because it is too large
View File

193
src/main/resources/static/assets/css/body-form-styles.css

@ -0,0 +1,193 @@
body {
margin: 0;
padding: 0;
min-height: 100vh; /* ensures gradient fills screen */
font-family: Arial, sans-serif;
background: linear-gradient(45deg, #f6f7fb, #4ca3b3);
background-repeat: no-repeat;
}
.form-title {
text-align: center;
margin-bottom: 20px;
font-size: 1.8rem;
color: #003366;
text-decoration: underline;
}
.header-row {
display: flex;
align-items: center;
gap: 20px;
margin-bottom: 20px;
}
.back-button {
background-color: white;
border: 2px solid black !important;
border-radius: 4px;
padding: 10px 20px;
border-radius: 5px;
font-size: 16px;
position: relative; /* override absolute */
color: #005f73;
cursor: pointer;
transition: background-color 0.3s, transform 0.2s;
}
.back-button:hover {
background: #4ca3b3;
transform: scale(1.05);
color: #000000;
}
.back-to-home-btn {
background-color: white;
border: 2px solid black !important;
border-radius: 4px;
padding: 10px 20px;
border-radius: 5px;
font-size: 16px;
position: relative; /* override absolute */
color: #005f73;
cursor: pointer;
transition: background-color 0.3s, transform 0.2s;
}
.back-to-home-btn:hover {
background: #4ca3b3;
transform: scale(1.05);
color: #000000;
}
.required {
color: red;
font-weight: bold;
}
.container {
padding: 20px;
max-width: 1200px;
margin: 0 auto;
width: 100%;
}
h2 {
color: #0B3954;
margin-bottom: 30px;
}
.form-container {
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-bottom: 20px;
}
.form-group {
margin-bottom: 20px;
display: flex;
flex-direction: column;
}
.form-group label {
margin-bottom: 5px;
font-weight: bold;
color: #333;
}
.form-control {
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.file-input {
display: flex;
gap: 10px;
}
.file-input button {
background: #f0f0f0;
border: none;
padding: 8px 15px;
border-radius: 4px;
}
.radio-group {
display: flex;
gap: 20px;
}
.radio-option {
display: flex;
align-items: center;
gap: 5px;
}
.submit-btn {
display: flex;
justify-content: center;
margin-top: 20px;
}
.submit-btn input[type="submit"]:disabled {
background-color: #cccccc; /* grey color */
color: #666666;
cursor: not-allowed;
}
.submit-btn input[type="submit"] {
background-color: #005f73;
color: #fff;
font-size: 1rem;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
transition: background 0.3s ease;
}
.submit-btn input[type="submit"]:hover {
background: #005f05;
}
/* Responsive Design for Tablets */
@media ( max-width : 768px) {
.form-row {
grid-template-columns: 1fr;
}
.form-container {
padding: 20px;
}
.form-title {
font-size: 1.5rem;
}
.form-control {
font-size: 1rem;
}
}
/* Responsive Design for Mobile */
@media ( max-width : 480px) {
.form-row {
grid-template-columns: 1fr;
}
.submit-btn input[type="submit"] {
width: 100%;
font-size: 1.2rem;
padding: 12px;
}
.form-container {
padding: 15px;
}
.form-title {
font-size: 1.2rem;
}
}

114
src/main/resources/static/assets/css/datatable-styles.css

@ -0,0 +1,114 @@
#custom-info {
display: flex;
align-items: center;
}
#custom-search {
display: flex;
justify-content: flex-end; /* pushes it to the right */
align-items: center;
}
#custom-length {
display: flex;
align-items: center;
gap: 8px; /* Add spacing between dropdown and label */
white-space: nowrap; /* Prevent wrapping */
}
.dataTables_length label {
display: flex;
align-items: center;
margin: 0;
gap: 5px; /* Adjust spacing between text and dropdown */
}
.dataTables_length select {
display: inline-block;
width: auto; /* Ensure dropdown adjusts to content */
}
#custom-pagination {
display: flex;
justify-content: flex-end; /* Align pagination to the right */
align-items: center;
}
.dataTables_filter {
display: flex;
align-items: center;
gap: 8px; /* space between label and input */
margin: 0;
}
.dataTables_filter label {
margin: 0;
font-weight: 500;
display: flex;
align-items: center;
color: black;
}
.dataTables_filter input {
border: 1px solid #ccc;
border-radius: 5px;
padding: 5px 8px;
margin-left: 5px; /* small gap after label */
outline: none;
width: 180px;
transition: all 0.2s ease;
}
/* Apply border-radius to the table */
.det-table {
width: 100%; /* Table only grows as needed */
border-collapse: collapse;
table-layout: auto; /* Browser decides column width based on content */
border-spacing: 0;
border: 2px solid #f0f0f0;
margin-top: 20px;
font-family: Arial, sans-serif;
font-size: 14px;
border-radius: 12px;
overflow: hidden;
}
.det-table th, .det-table td {
border: 1px solid #e0e0e0;
padding: 6px 10px; /* Small padding keeps things compact */
white-space: nowrap; /* Prevents text wrapping → keeps columns narrow */
text-align: center;
}
.det-table td.no-word-wrap {
white-space: nowrap;
}
.det-table th {
background-color: #005f73;
color: white;
text-align: center;
}
.det-table tr:nth-child(even) {
background-color: #f9f9f9;
}
.det-table tr:hover {
background-color: #f1f1f1;
}
.det-table th, .det-table td {
text-align: center;
}
@media (max-width: 767.98px) {
.btn-group {
width: 100%;
justify-content: center;
}
.btn-group .btn {
width: 48%; /* make buttons fill container nicely */
margin: 1px;
}
}

77
src/main/resources/static/assets/css/header-styles.css

@ -0,0 +1,77 @@
.header {
background-color: #005f73;
padding: 10px 20px;
display: flex;
align-items: center;
/* justify-content: space-between; */
position: relative;
color: white;
}
.header .logo-left img {
height: 60px;
width: 60px;
margin-right: 10px;
}
.header .title {
text-align: center;
/* flex-grow: 1; */
position: absolute;
transform: translateX(-50%);
left: 50%
}
.header .title h2 {
margin: 0;
font-size: 1.5rem;
}
.header .logos-right img {
height: 60px;
margin-left: 10px;
}
.header .logo-left {
display: flex;
align-items: center;
}
.header .logo-text {
display: flex;
flex-direction: column;
line-height: 1;
}
.header .logo-text h2 {
margin: 0;
font-size: 0.8rem;
color: white;
font-weight: bold;
}
.header .logo-text p {
margin: 0;
font-size: 0.5rem;
color: white;
}
@media ( max-width : 480px) {
.header {
flex-direction: column;
align-items: flex-start;
position: relative;
}
.header .logo-left img {
margin-bottom: 10px;
}
.header .logos-right img {
margin-bottom: 10px;
}
.header .title {
position: static; /* Reset absolute positioning */
transform: none; /* Remove the translate effect */
margin-top: 10px;
text-align: center; /* Ensure the text stays centered */
}
}

295
src/main/resources/static/assets/css/home-styles.css

@ -0,0 +1,295 @@
.button-row {
display: flex;
justify-content: center;
gap: 40px;
flex-wrap: wrap; /* allows wrapping */
margin-top: 20px;
text-align: center;
}
/* Center heading & align right-side search neatly */
.row.align-items-center.gx-3.justify-content-between {
display: flex;
align-items: center;
flex-wrap: wrap;
}
.form-title {
margin: 0;
font-weight: 600;
text-align: center;
width: 100%;
}
/* Ensure dropdown + search align nicely */
.col-md-6.d-flex.justify-content-md-end {
display: flex;
justify-content: flex-end;
align-items: center;
gap: 10px;
}
/* Center content properly in larger screens */
.form-container {
max-width: 1200px;
margin: 0 auto; /* centers horizontally */
}
.form-control {
width: auto !important;
flex: 0 0 auto;
}
.custom-button {
padding: 15px 30px;
position: relative; /* Important: makes absolute badge work */
background: white;
border: 1px solid #ccc;
border-radius: 10px;
font-size: 1rem;
color: #005f73;
cursor: pointer;
transition: background-color 0.3s, transform 0.2s;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
flex: 1 1 calc(33.333% - 40px); /* 3 per row, minus gap space */
max-width: 300px; /* optional: limit button width */
min-width: 200px; /* optional: keep from shrinking too small */
}
.custom-button:hover {
background: #4ca3b3;
transform: scale(1.05);
color: #000000;
}
.custom-button:active {
transform: scale(0.95);
}
#search-input2 {
min-width: 80px;
border-radius: 8px;
}
#search-input {
width: 250px;
max-width: 100%;
padding: 8px 12px;
border: 1px solid #ccc;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
transition: all 0.2s ease;
}
#search-input:focus {
outline: none;
border-color: #4ca3b3;
box-shadow: 0 3px 8px rgba(76, 163, 179, 0.3);
}
/* === Login Modal Overlay === */
.login-modal-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 1050;
align-items: center;
justify-content: center;
}
/* === Modal Box === */
.login-modal-box {
background: #fff;
width: 400px;
max-width: 90%;
border-radius: 8px;
padding: 25px 30px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
position: relative;
animation: fadeIn 0.3s ease;
}
/* === Close Button === */
.close-btn {
position: absolute;
top: 10px;
right: 15px;
font-size: 24px;
color: #666;
cursor: pointer;
}
.close-btn:hover {
color: #000;
}
/* === Title === */
.login-title {
text-align: center;
margin-bottom: 20px;
font-size: 22px;
color: #333;
font-weight: 600;
}
.login-btn-send {
background-color: #005f73;
color: #fff;
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: 600;
transition: background 0.2s ease;
}
.login-btn-send:hover {
background-color: #4ca3b3;
}
/* === Form === */
.login-form .form-label {
display: block;
font-size: 15px;
color: #444;
margin-bottom: 6px;
}
.req {
color: red;
}
.form-input {
width: 100%;
padding: 10px 12px;
font-size: 15px;
border: 1px solid #ccc;
border-radius: 4px;
outline: none;
}
.form-input:focus {
border-color: #00bcd4;
box-shadow: 0 0 4px rgba(0, 188, 212, 0.4);
}
/* === Button Row === */
.btn-row {
margin-top: 20px;
display: flex;
justify-content: center;
gap: 12px;
}
/* === Animation states === */
.login-modal-overlay.show {
animation: fadeIn 0.3s ease forwards;
}
.login-modal-overlay.hide {
animation: fadeOut 0.3s ease forwards;
}
.url-num-badge {
position: absolute;
top: -12px;
left: -12px;
background-color: #005f73; /* or any color */
color: white;
font-size: 14px;
width: 28px;
height: 28px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
z-index: 10;
}
/* === Fade Animations === */
@keyframes fadeIn {
from {
transform: scale(0.95);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
@keyframes fadeOut {
from {
transform: scale(1);
opacity: 1;
}
to {
transform: scale(0.95);
opacity: 0;
}
}
/* Fix heading centering in large screens */
@media (min-width: 992px) {
.row.align-items-center.gy-3.justify-content-center.text-center {
justify-content: space-between !important;
}
.row.align-items-center.gy-3.justify-content-center.text-center .col-md-6:first-child {
text-align: center !important;
flex: 1 1 100%;
max-width: 100%;
}
.row.align-items-center.gy-3.justify-content-center.text-center .col-md-6:last-child {
flex: 1 1 auto;
max-width: fit-content;
margin-left: auto;
}
}
/* Tablet screens - shrink more */
@media (max-width : 768px) {
.form-title {
font-size: 1.2rem !important;
}
.button-row {
justify-content: center;
align-items: center;
flex-direction: column;
gap: 15px; /* tighter spacing for small screens */
}
.custom-button {
flex: none;
width: 80%; /* nicely centered buttons */
max-width: 320px;
min-width: unset; /* remove constraint */
}
}
/* Responsive tweak for small screens */
@media (max-width: 576px) {
.form-title {
font-size: 1.1rem !important;
text-align: center;
}
#search-input {
width: 100%;
max-width: 280px;
}
#search-input2 {
width: 100% !important;
max-width: 100px;
}
}

272
src/main/resources/static/assets/css/login-form-styles.css

@ -0,0 +1,272 @@
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 100;
src: url("../poppins-23.0/fonts/devanagari/poppins-v23-devanagari-700.woff2") format("woff2");
}
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 200;
src: url("../poppins-23.0/fonts/devanagari/poppins-v23-devanagari-200.woff2")
format("woff2");
}
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 300;
src: url("../poppins-23.0/fonts/devanagari/poppins-v23-devanagari-300.woff2")
format("woff2");
}
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 400;
src: url("../poppins-23.0/fonts/devanagari/poppins-v23-devanagari-regular.woff2")
format("woff2");
}
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 500;
src: url("../poppins-23.0/fonts/devanagari/poppins-v23-devanagari-500.woff2")
format("woff2");
}
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 600;
src: url("../poppins-23.0/fonts/devanagari/poppins-v23-devanagari-600.woff2")
format("woff2");
}
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 700;
src: url("../poppins-23.0/fonts/devanagari/poppins-v23-devanagari-700.woff2")
format("woff2");
}
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 100;
src: url("../poppins-23.0/fonts/latin-ext/poppins-v23-latin-ext-100.woff2")
format("woff2");
}
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 200;
src: url("../poppins-23.0/fonts/latin-ext/poppins-v23-latin-ext-200.woff2")
format("woff2");
}
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 300;
src: url("../poppins-23.0/fonts/latin-ext/poppins-v23-latin-ext-300.woff2")
format("woff2");
}
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 400;
src: url("../poppins-23.0/fonts/latin-ext/poppins-v23-latin-ext-regular.woff2")
format("woff2");
}
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 500;
src: url("../poppins-23.0/fonts/latin-ext/poppins-v23-latin-ext-500.woff2")
format("woff2");
}
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 600;
src: url("../poppins-23.0/fonts/latin-ext/poppins-v23-latin-ext-600.woff2")
format("woff2");
}
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 700;
src: url("../poppins-23.0/fonts/latin-ext/poppins-v23-latin-ext-700.woff2")
format("woff2");
}
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 100;
src: url("../poppins-23.0/fonts/latin/poppins-v23-latin-100.woff2")
format("woff2");
}
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 200;
src: url("../poppins-23.0/fonts/latin/poppins-v23-latin-200.woff2")
format("woff2");
}
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 300;
src: url("../poppins-23.0/fonts/latin/poppins-v23-latin-300.woff2")
format("woff2");
}
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 400;
src: url("../poppins-23.0/fonts/latin/poppins-v23-latin-regular.woff2")
format("woff2");
}
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 500;
src: url("../poppins-23.0/fonts/latin/poppins-v23-latin-500.woff2")
format("woff2");
}
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 600;
src: url("../poppins-23.0/fonts/latin/poppins-v23-latin-600.woff2")
format("woff2");
}
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 700;
src: url("../poppins-23.0/fonts/latin/poppins-v23-latin-700.woff2")
format("woff2");
}
* {
margin: 0;
border: 0;
padding: 0;
box-sizing: border-box;
font-family: "Poppins", sans-serif;
}
body {
background: linear-gradient(45deg, #f6f7fb, #4ca3b3);
background-repeat: no-repeat;
min-height: 100vh;
min-width: 100vw;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
margin: 0;
padding: 0;
}
div.container {
background: white;
min-width: 320px;
min-height: 33vh;
padding: 2rem;
box-shadow: 5px 5px 15px rgba(0, 0, 0, 0.2);
border-radius: 8px;
}
div h2 {
font-weight: 600;
margin-bottom: 2rem;
position: relative;
}
.container h3,
.container h4 {
text-align: center;
}
div h2::before {
content: "";
position: absolute;
height: 4px;
width: 25px;
bottom: 3px;
left: 0;
border-radius: 8px;
background: #005f73;
}
form {
display: flex;
flex-direction: column;
}
.input-field {
position: relative;
margin-bottom: 1.5rem; /* instead of targeting only first-child */
}
.input-field .underline::before {
content: "";
position: absolute;
height: 1px;
width: 100%;
bottom: -5px;
left: 0;
background: rgba(0, 0, 0, 0.2);
}
.input-field .underline::after {
content: "";
position: absolute;
height: 1px;
width: 100%;
bottom: -5px;
left: 0;
background: linear-gradient(45deg, #8e2de2, #4a00e0);
transform: scaleX(0);
transition: all 0.3s ease-in-out;
transform-origin: left;
}
.input-field input:focus ~ .underline::after {
transform: scaleX(1);
}
.input-field input {
outline: none;
font-size: 0.9rem;
color: rgba(0, 0, 0, 0.7);
width: 100%;
}
.input-field input::placeholder {
color: rgba(0, 0, 0, 0.5);
}
form input[type="submit"] {
margin-top: 2rem;
padding: 0.4rem;
width: 100%;
background: #005f73;
cursor: pointer;
color: white;
font-size: 0.9rem;
font-weight: 300;
border-radius: 4px;
transition: all 0.3s ease;
}
form input[type="submit"]:hover {
letter-spacing: 0.5px;
background: #005f05;
}
.small-logo {
width: 60px;
height: 60px;
display: block;
margin: 0 auto; /* Centers the image */
}

532
src/main/resources/static/assets/datatables-1.13.6/css/dataTables.bootstrap5.min.css

@ -0,0 +1,532 @@
:root {
--dt-row-selected: 13, 110, 253;
--dt-row-selected-text: 255, 255, 255;
--dt-row-selected-link: 9, 10, 11;
--dt-row-stripe: 0, 0, 0;
--dt-row-hover: 0, 0, 0;
--dt-column-ordering: 0, 0, 0;
--dt-html-background: white;
}
:root.dark {
--dt-html-background: rgb(33, 37, 41);
}
table.dataTable td.dt-control {
text-align: center;
cursor: pointer;
}
table.dataTable td.dt-control:before {
display: inline-block;
color: rgba(0, 0, 0, 0.5);
content: "►";
}
table.dataTable tr.dt-hasChild td.dt-control:before {
content: "▼";
}
html.dark table.dataTable td.dt-control:before {
color: rgba(255, 255, 255, 0.5);
}
html.dark table.dataTable tr.dt-hasChild td.dt-control:before {
color: rgba(255, 255, 255, 0.5);
}
table.dataTable thead > tr > th.sorting,
table.dataTable thead > tr > th.sorting_asc,
table.dataTable thead > tr > th.sorting_desc,
table.dataTable thead > tr > th.sorting_asc_disabled,
table.dataTable thead > tr > th.sorting_desc_disabled,
table.dataTable thead > tr > td.sorting,
table.dataTable thead > tr > td.sorting_asc,
table.dataTable thead > tr > td.sorting_desc,
table.dataTable thead > tr > td.sorting_asc_disabled,
table.dataTable thead > tr > td.sorting_desc_disabled {
cursor: pointer;
position: relative;
padding-right: 26px;
}
table.dataTable thead > tr > th.sorting:before,
table.dataTable thead > tr > th.sorting:after,
table.dataTable thead > tr > th.sorting_asc:before,
table.dataTable thead > tr > th.sorting_asc:after,
table.dataTable thead > tr > th.sorting_desc:before,
table.dataTable thead > tr > th.sorting_desc:after,
table.dataTable thead > tr > th.sorting_asc_disabled:before,
table.dataTable thead > tr > th.sorting_asc_disabled:after,
table.dataTable thead > tr > th.sorting_desc_disabled:before,
table.dataTable thead > tr > th.sorting_desc_disabled:after,
table.dataTable thead > tr > td.sorting:before,
table.dataTable thead > tr > td.sorting:after,
table.dataTable thead > tr > td.sorting_asc:before,
table.dataTable thead > tr > td.sorting_asc:after,
table.dataTable thead > tr > td.sorting_desc:before,
table.dataTable thead > tr > td.sorting_desc:after,
table.dataTable thead > tr > td.sorting_asc_disabled:before,
table.dataTable thead > tr > td.sorting_asc_disabled:after,
table.dataTable thead > tr > td.sorting_desc_disabled:before,
table.dataTable thead > tr > td.sorting_desc_disabled:after {
position: absolute;
display: block;
opacity: 0.125;
right: 10px;
line-height: 9px;
font-size: 0.8em;
}
table.dataTable thead > tr > th.sorting:before,
table.dataTable thead > tr > th.sorting_asc:before,
table.dataTable thead > tr > th.sorting_desc:before,
table.dataTable thead > tr > th.sorting_asc_disabled:before,
table.dataTable thead > tr > th.sorting_desc_disabled:before,
table.dataTable thead > tr > td.sorting:before,
table.dataTable thead > tr > td.sorting_asc:before,
table.dataTable thead > tr > td.sorting_desc:before,
table.dataTable thead > tr > td.sorting_asc_disabled:before,
table.dataTable thead > tr > td.sorting_desc_disabled:before {
bottom: 50%;
content: "▲";
content: "▲"/"";
}
table.dataTable thead > tr > th.sorting:after,
table.dataTable thead > tr > th.sorting_asc:after,
table.dataTable thead > tr > th.sorting_desc:after,
table.dataTable thead > tr > th.sorting_asc_disabled:after,
table.dataTable thead > tr > th.sorting_desc_disabled:after,
table.dataTable thead > tr > td.sorting:after,
table.dataTable thead > tr > td.sorting_asc:after,
table.dataTable thead > tr > td.sorting_desc:after,
table.dataTable thead > tr > td.sorting_asc_disabled:after,
table.dataTable thead > tr > td.sorting_desc_disabled:after {
top: 50%;
content: "▼";
content: "▼"/"";
}
table.dataTable thead > tr > th.sorting_asc:before,
table.dataTable thead > tr > th.sorting_desc:after,
table.dataTable thead > tr > td.sorting_asc:before,
table.dataTable thead > tr > td.sorting_desc:after {
opacity: 0.6;
}
table.dataTable thead > tr > th.sorting_desc_disabled:after,
table.dataTable thead > tr > th.sorting_asc_disabled:before,
table.dataTable thead > tr > td.sorting_desc_disabled:after,
table.dataTable thead > tr > td.sorting_asc_disabled:before {
display: none;
}
table.dataTable thead > tr > th:active,
table.dataTable thead > tr > td:active {
outline: none;
}
div.dataTables_scrollBody > table.dataTable > thead > tr > th:before,
div.dataTables_scrollBody > table.dataTable > thead > tr > th:after,
div.dataTables_scrollBody > table.dataTable > thead > tr > td:before,
div.dataTables_scrollBody > table.dataTable > thead > tr > td:after {
display: none;
}
div.dataTables_processing {
position: absolute;
top: 50%;
left: 50%;
width: 200px;
margin-left: -100px;
margin-top: -26px;
text-align: center;
padding: 2px;
}
div.dataTables_processing > div:last-child {
position: relative;
width: 80px;
height: 15px;
margin: 1em auto;
}
div.dataTables_processing > div:last-child > div {
position: absolute;
top: 0;
width: 13px;
height: 13px;
border-radius: 50%;
background: rgb(13, 110, 253);
background: rgb(var(--dt-row-selected));
animation-timing-function: cubic-bezier(0, 1, 1, 0);
}
div.dataTables_processing > div:last-child > div:nth-child(1) {
left: 8px;
animation: datatables-loader-1 0.6s infinite;
}
div.dataTables_processing > div:last-child > div:nth-child(2) {
left: 8px;
animation: datatables-loader-2 0.6s infinite;
}
div.dataTables_processing > div:last-child > div:nth-child(3) {
left: 32px;
animation: datatables-loader-2 0.6s infinite;
}
div.dataTables_processing > div:last-child > div:nth-child(4) {
left: 56px;
animation: datatables-loader-3 0.6s infinite;
}
@keyframes datatables-loader-1 {
0% {
transform: scale(0);
}
100% {
transform: scale(1);
}
}
@keyframes datatables-loader-3 {
0% {
transform: scale(1);
}
100% {
transform: scale(0);
}
}
@keyframes datatables-loader-2 {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(24px, 0);
}
}
table.dataTable.nowrap th,
table.dataTable.nowrap td {
white-space: nowrap;
}
table.dataTable th.dt-left,
table.dataTable td.dt-left {
text-align: left;
}
table.dataTable th.dt-center,
table.dataTable td.dt-center,
table.dataTable td.dataTables_empty {
text-align: center;
}
table.dataTable th.dt-right,
table.dataTable td.dt-right {
text-align: right;
}
table.dataTable th.dt-justify,
table.dataTable td.dt-justify {
text-align: justify;
}
table.dataTable th.dt-nowrap,
table.dataTable td.dt-nowrap {
white-space: nowrap;
}
table.dataTable thead th,
table.dataTable thead td,
table.dataTable tfoot th,
table.dataTable tfoot td {
text-align: left;
}
table.dataTable thead th.dt-head-left,
table.dataTable thead td.dt-head-left,
table.dataTable tfoot th.dt-head-left,
table.dataTable tfoot td.dt-head-left {
text-align: left;
}
table.dataTable thead th.dt-head-center,
table.dataTable thead td.dt-head-center,
table.dataTable tfoot th.dt-head-center,
table.dataTable tfoot td.dt-head-center {
text-align: center;
}
table.dataTable thead th.dt-head-right,
table.dataTable thead td.dt-head-right,
table.dataTable tfoot th.dt-head-right,
table.dataTable tfoot td.dt-head-right {
text-align: right;
}
table.dataTable thead th.dt-head-justify,
table.dataTable thead td.dt-head-justify,
table.dataTable tfoot th.dt-head-justify,
table.dataTable tfoot td.dt-head-justify {
text-align: justify;
}
table.dataTable thead th.dt-head-nowrap,
table.dataTable thead td.dt-head-nowrap,
table.dataTable tfoot th.dt-head-nowrap,
table.dataTable tfoot td.dt-head-nowrap {
white-space: nowrap;
}
table.dataTable tbody th.dt-body-left,
table.dataTable tbody td.dt-body-left {
text-align: left;
}
table.dataTable tbody th.dt-body-center,
table.dataTable tbody td.dt-body-center {
text-align: center;
}
table.dataTable tbody th.dt-body-right,
table.dataTable tbody td.dt-body-right {
text-align: right;
}
table.dataTable tbody th.dt-body-justify,
table.dataTable tbody td.dt-body-justify {
text-align: justify;
}
table.dataTable tbody th.dt-body-nowrap,
table.dataTable tbody td.dt-body-nowrap {
white-space: nowrap;
} /*! Bootstrap 5 integration for DataTables
*
* ©2020 SpryMedia Ltd, all rights reserved.
* License: MIT datatables.net/license/mit
*/
table.dataTable {
clear: both;
margin-top: 6px !important;
margin-bottom: 6px !important;
max-width: none !important;
border-collapse: separate !important;
border-spacing: 0;
}
table.dataTable td,
table.dataTable th {
-webkit-box-sizing: content-box;
box-sizing: content-box;
}
table.dataTable td.dataTables_empty,
table.dataTable th.dataTables_empty {
text-align: center;
}
table.dataTable.nowrap th,
table.dataTable.nowrap td {
white-space: nowrap;
}
table.dataTable.table-striped > tbody > tr:nth-of-type(2n + 1) > * {
box-shadow: none;
}
table.dataTable > tbody > tr {
background-color: transparent;
}
table.dataTable > tbody > tr.selected > * {
box-shadow: inset 0 0 0 9999px rgb(13, 110, 253);
box-shadow: inset 0 0 0 9999px rgb(var(--dt-row-selected));
color: rgb(255, 255, 255);
color: rgb(var(--dt-row-selected-text));
}
table.dataTable > tbody > tr.selected a {
color: rgb(9, 10, 11);
color: rgb(var(--dt-row-selected-link));
}
table.dataTable.table-striped > tbody > tr.odd > * {
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-stripe), 0.05);
}
table.dataTable.table-striped > tbody > tr.odd.selected > * {
box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.95);
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.95);
}
table.dataTable.table-hover > tbody > tr:hover > * {
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.075);
}
table.dataTable.table-hover > tbody > tr.selected:hover > * {
box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.975);
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.975);
}
div.dataTables_wrapper div.dataTables_length label {
font-weight: normal;
text-align: left;
white-space: nowrap;
}
div.dataTables_wrapper div.dataTables_length select {
width: auto;
display: inline-block;
}
div.dataTables_wrapper div.dataTables_filter {
text-align: right;
}
div.dataTables_wrapper div.dataTables_filter label {
font-weight: normal;
white-space: nowrap;
text-align: left;
}
div.dataTables_wrapper div.dataTables_filter input {
margin-left: 0.5em;
display: inline-block;
width: auto;
}
div.dataTables_wrapper div.dataTables_info {
padding-top: 0.85em;
}
div.dataTables_wrapper div.dataTables_paginate {
margin: 0;
white-space: nowrap;
text-align: right;
}
div.dataTables_wrapper div.dataTables_paginate ul.pagination {
margin: 2px 0;
white-space: nowrap;
justify-content: flex-end;
}
div.dataTables_wrapper div.dt-row {
position: relative;
}
div.dataTables_scrollHead table.dataTable {
margin-bottom: 0 !important;
}
div.dataTables_scrollBody > table {
border-top: none;
margin-top: 0 !important;
margin-bottom: 0 !important;
}
div.dataTables_scrollBody > table > thead .sorting:before,
div.dataTables_scrollBody > table > thead .sorting_asc:before,
div.dataTables_scrollBody > table > thead .sorting_desc:before,
div.dataTables_scrollBody > table > thead .sorting:after,
div.dataTables_scrollBody > table > thead .sorting_asc:after,
div.dataTables_scrollBody > table > thead .sorting_desc:after {
display: none;
}
div.dataTables_scrollBody > table > tbody tr:first-child th,
div.dataTables_scrollBody > table > tbody tr:first-child td {
border-top: none;
}
div.dataTables_scrollFoot > .dataTables_scrollFootInner {
box-sizing: content-box;
}
div.dataTables_scrollFoot > .dataTables_scrollFootInner > table {
margin-top: 0 !important;
border-top: none;
}
@media screen and (max-width: 767px) {
div.dataTables_wrapper div.dataTables_length,
div.dataTables_wrapper div.dataTables_filter,
div.dataTables_wrapper div.dataTables_info,
div.dataTables_wrapper div.dataTables_paginate {
text-align: center;
}
div.dataTables_wrapper div.dataTables_paginate ul.pagination {
justify-content: center !important;
}
}
table.dataTable.table-sm > thead > tr > th:not(.sorting_disabled) {
padding-right: 20px;
}
table.table-bordered.dataTable {
border-right-width: 0;
}
table.table-bordered.dataTable thead tr:first-child th,
table.table-bordered.dataTable thead tr:first-child td {
border-top-width: 1px;
}
table.table-bordered.dataTable th,
table.table-bordered.dataTable td {
border-left-width: 0;
}
table.table-bordered.dataTable th:first-child,
table.table-bordered.dataTable th:first-child,
table.table-bordered.dataTable td:first-child,
table.table-bordered.dataTable td:first-child {
border-left-width: 1px;
}
table.table-bordered.dataTable th:last-child,
table.table-bordered.dataTable th:last-child,
table.table-bordered.dataTable td:last-child,
table.table-bordered.dataTable td:last-child {
border-right-width: 1px;
}
table.table-bordered.dataTable th,
table.table-bordered.dataTable td {
border-bottom-width: 1px;
}
div.dataTables_scrollHead table.table-bordered {
border-bottom-width: 0;
}
div.table-responsive > div.dataTables_wrapper > div.row {
margin: 0;
}
div.table-responsive
> div.dataTables_wrapper
> div.row
> div[class^="col-"]:first-child {
padding-left: 0;
}
div.table-responsive
> div.dataTables_wrapper
> div.row
> div[class^="col-"]:last-child {
padding-right: 0;
}
:root[data-bs-theme="dark"] {
--dt-row-hover: 255, 255, 255;
--dt-row-stripe: 255, 255, 255;
--dt-column-ordering: 255, 255, 255;
}

80
src/main/resources/static/assets/datatables-1.13.6/js/dataTables.bootstrap5.min.js

@ -0,0 +1,80 @@
/*! DataTables Bootstrap 5 integration
* 2020 SpryMedia Ltd - datatables.net/license
*/
! function(t) {
var n, r;
"function" == typeof define && define.amd ? define(["jquery", "datatables.net"], function(e) {
return t(e, window, document)
}) : "object" == typeof exports ? (n = require("jquery"), r = function(e, a) {
a.fn.dataTable || require("datatables.net")(e, a)
}, "undefined" == typeof window ? module.exports = function(e, a) {
return e = e || window, a = a || n(e), r(e, a), t(a, 0, e.document)
} : (r(window, n), module.exports = t(n, window, window.document))) : t(jQuery, window, document)
}(function(x, e, r, o) {
"use strict";
var i = x.fn.dataTable;
return x.extend(!0, i.defaults, {
dom: "<'row'<'col-sm-12 col-md-6'l><'col-sm-12 col-md-6'f>><'row dt-row'<'col-sm-12'tr>><'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
renderer: "bootstrap"
}), x.extend(i.ext.classes, {
sWrapper: "dataTables_wrapper dt-bootstrap5",
sFilterInput: "form-control form-control-sm",
sLengthSelect: "form-select form-select-sm",
sProcessing: "dataTables_processing card",
sPageButton: "paginate_button page-item"
}), i.ext.renderer.pageButton.bootstrap = function(d, e, s, a, l, c) {
function u(e, a) {
for (var t, n, r = function(e) {
e.preventDefault(), x(e.currentTarget).hasClass("disabled") || b.page() == e.data.action || b.page(e.data.action).draw("page")
}, o = 0, i = a.length; o < i; o++)
if (t = a[o], Array.isArray(t)) u(e, t);
else {
switch (f = p = "", t) {
case "ellipsis":
p = "&#x2026;", f = "disabled";
break;
case "first":
p = g.sFirst, f = t + (0 < l ? "" : " disabled");
break;
case "previous":
p = g.sPrevious, f = t + (0 < l ? "" : " disabled");
break;
case "next":
p = g.sNext, f = t + (l < c - 1 ? "" : " disabled");
break;
case "last":
p = g.sLast, f = t + (l < c - 1 ? "" : " disabled");
break;
default:
p = t + 1, f = l === t ? "active" : ""
}
p && (n = -1 !== f.indexOf("disabled"), n = x("<li>", {
class: m.sPageButton + " " + f,
id: 0 === s && "string" == typeof t ? d.sTableId + "_" + t : null
}).append(x("<a>", {
href: n ? null : "#",
"aria-controls": d.sTableId,
"aria-disabled": n ? "true" : null,
"aria-label": w[t],
role: "link",
"aria-current": "active" === f ? "page" : null,
"data-dt-idx": t,
tabindex: n ? -1 : d.iTabIndex,
class: "page-link"
}).html(p)).appendTo(e), d.oApi._fnBindAction(n, {
action: t
}, r))
}
}
var p, f, t, b = new i.Api(d),
m = d.oClasses,
g = d.oLanguage.oPaginate,
w = d.oLanguage.oAria.paginate || {},
e = x(e);
try {
t = e.find(r.activeElement).data("dt-idx")
} catch (e) { }
var n = e.children("ul.pagination");
n.length ? n.empty() : n = e.html("<ul/>").children("ul").addClass("pagination"), u(n, a), t !== o && e.find("[data-dt-idx=" + t + "]").trigger("focus")
}, i
});

2990
src/main/resources/static/assets/datatables-1.13.6/js/jquery.dataTables.min.js
File diff suppressed because it is too large
View File

248
src/main/resources/static/assets/datatables-buttons-2.4.1/css/buttons.bootstrap5.min.css

@ -0,0 +1,248 @@
@keyframes dtb-spinner {
100% {
transform: rotate(360deg);
}
}
@-o-keyframes dtb-spinner {
100% {
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@-ms-keyframes dtb-spinner {
100% {
-ms-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@-webkit-keyframes dtb-spinner {
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@-moz-keyframes dtb-spinner {
100% {
-moz-transform: rotate(360deg);
transform: rotate(360deg);
}
}
div.dataTables_wrapper {
position: relative;
}
div.dt-buttons {
position: initial;
}
div.dt-buttons .dt-button {
overflow: hidden;
text-overflow: ellipsis;
}
div.dt-button-info {
position: fixed;
top: 50%;
left: 50%;
width: 400px;
margin-top: -100px;
margin-left: -200px;
background-color: white;
border-radius: 0.75em;
box-shadow: 3px 4px 10px 1px rgba(0, 0, 0, 0.8);
text-align: center;
z-index: 2003;
overflow: hidden;
}
div.dt-button-info h2 {
padding: 2rem 2rem 1rem 2rem;
margin: 0;
font-weight: normal;
}
div.dt-button-info > div {
padding: 1em 2em 2em 2em;
}
div.dtb-popover-close {
position: absolute;
top: 6px;
right: 6px;
width: 22px;
height: 22px;
text-align: center;
border-radius: 3px;
cursor: pointer;
z-index: 2003;
}
button.dtb-hide-drop {
display: none !important;
}
div.dt-button-collection-title {
text-align: center;
padding: 0.3em 0 0.5em;
margin-left: 0.5em;
margin-right: 0.5em;
font-size: 0.9em;
}
div.dt-button-collection-title:empty {
display: none;
}
span.dt-button-spacer {
display: inline-block;
margin: 0.5em;
white-space: nowrap;
}
span.dt-button-spacer.bar {
border-left: 1px solid rgba(0, 0, 0, 0.3);
vertical-align: middle;
padding-left: 0.5em;
}
span.dt-button-spacer.bar:empty {
height: 1em;
width: 1px;
padding-left: 0;
}
div.dt-button-collection .dt-button-active {
padding-right: 3em;
}
div.dt-button-collection .dt-button-active:after {
position: absolute;
top: 50%;
margin-top: -10px;
right: 1em;
display: inline-block;
content: "✓";
color: inherit;
}
div.dt-button-collection .dt-button-active.dt-button-split {
padding-right: 0;
}
div.dt-button-collection .dt-button-active.dt-button-split:after {
display: none;
}
div.dt-button-collection .dt-button-active.dt-button-split > *:first-child {
padding-right: 3em;
}
div.dt-button-collection
.dt-button-active.dt-button-split
> *:first-child:after {
position: absolute;
top: 50%;
margin-top: -10px;
right: 1em;
display: inline-block;
content: "✓";
color: inherit;
}
div.dt-button-collection .dt-button-active-a a {
padding-right: 3em;
}
div.dt-button-collection .dt-button-active-a a:after {
position: absolute;
right: 1em;
display: inline-block;
content: "✓";
color: inherit;
}
div.dt-button-collection span.dt-button-spacer {
width: 100%;
font-size: 0.9em;
text-align: center;
margin: 0.5em 0;
}
div.dt-button-collection span.dt-button-spacer:empty {
height: 0;
width: 100%;
}
div.dt-button-collection span.dt-button-spacer.bar {
border-left: none;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
padding-left: 0;
}
html.dark div.dt-button-info {
background-color: var(--dt-html-background);
border: 1px solid rgba(255, 255, 255, 0.15);
}
div.dt-buttons div.btn-group {
position: initial;
}
div.dt-buttons div.dropdown-menu {
margin-top: 4px;
}
div.dt-buttons div.dropdown-menu .dt-button {
position: relative;
}
div.dt-buttons div.dropdown-menu div.dt-button-split {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
align-content: flex-start;
align-items: stretch;
}
div.dt-buttons div.dropdown-menu div.dt-button-split a:first-child {
min-width: auto;
flex: 1 0 50px;
padding-right: 0;
}
div.dt-buttons div.dropdown-menu div.dt-button-split button:last-child {
min-width: 33px;
flex: 0;
background: transparent;
border: none;
line-height: 1rem;
color: var(--bs-dropdown-link-color);
padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);
}
div.dt-buttons div.dropdown-menu div.dt-button-split button:last-child:hover {
color: var(--bs-dropdown-link-hover-color);
background-color: var(--bs-dropdown-link-hover-bg);
}
div.dt-buttons div.dropdown-menu div.dt-button-split button:last-child:after {
position: relative;
left: -3px;
}
div.dt-buttons span.dt-button-spacer.empty {
margin: 1px;
}
div.dt-buttons span.dt-button-spacer.bar:empty {
height: inherit;
}
div.dt-buttons .btn.processing {
color: rgba(0, 0, 0, 0.2);
}
div.dt-buttons .btn.processing:after {
position: absolute;
top: 50%;
left: 50%;
width: 16px;
height: 16px;
margin: -8px 0 0 -8px;
box-sizing: border-box;
display: block;
content: " ";
border: 2px solid rgb(40, 40, 40);
border-radius: 50%;
border-left-color: transparent;
border-right-color: transparent;
animation: dtb-spinner 1500ms infinite linear;
-o-animation: dtb-spinner 1500ms infinite linear;
-ms-animation: dtb-spinner 1500ms infinite linear;
-webkit-animation: dtb-spinner 1500ms infinite linear;
-moz-animation: dtb-spinner 1500ms infinite linear;
}
div.dt-button-background {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 999;
}
@media screen and (max-width: 767px) {
div.dt-buttons {
float: none;
width: 100%;
text-align: center;
margin-bottom: 0.5em;
}
div.dt-buttons a.btn {
float: none;
}
}

70
src/main/resources/static/assets/datatables-buttons-2.4.1/js/buttons.bootstrap5.min.js

@ -0,0 +1,70 @@
/*! Bootstrap integration for DataTables' Buttons
* © SpryMedia Ltd - datatables.net/license
*/
! function(o) {
var e, a;
"function" == typeof define && define.amd ? define(["jquery", "datatables.net-bs5", "datatables.net-buttons"], function(t) {
return o(t, window, document)
}) : "object" == typeof exports ? (e = require("jquery"), a = function(t, n) {
n.fn.dataTable || require("datatables.net-bs5")(t, n), n.fn.dataTable.Buttons || require("datatables.net-buttons")(t, n)
}, "undefined" == typeof window ? module.exports = function(t, n) {
return t = t || window, n = n || e(t), a(t, n), o(n, 0, t.document)
} : (a(window, e), module.exports = o(e, window, window.document))) : o(jQuery, window, document)
}(function(o, t, n, e) {
"use strict";
var a = o.fn.dataTable;
return o.extend(!0, a.Buttons.defaults, {
dom: {
container: {
className: "dt-buttons btn-group flex-wrap"
},
button: {
className: "btn btn-secondary",
active: "active"
},
collection: {
action: {
dropHtml: ""
},
container: {
tag: "div",
className: "dropdown-menu dt-button-collection"
},
closeButton: !1,
button: {
tag: "a",
className: "dt-button dropdown-item",
active: "dt-button-active",
disabled: "disabled",
spacer: {
className: "dropdown-divider",
tag: "hr"
}
}
},
split: {
action: {
tag: "a",
className: "btn btn-secondary dt-button-split-drop-button",
closeButton: !1
},
dropdown: {
tag: "button",
dropHtml: "",
className: "btn btn-secondary dt-button-split-drop dropdown-toggle dropdown-toggle-split",
closeButton: !1,
align: "split-left",
splitAlignClass: "dt-button-split-left"
},
wrapper: {
tag: "div",
className: "dt-button-split btn-group",
closeButton: !1
}
}
},
buttonCreated: function(t, n) {
return t.buttons ? o('<div class="btn-group"/>').append(n) : n
}
}), a.ext.buttons.collection.className += " dropdown-toggle", a.ext.buttons.collection.rightAlignClassName = "dropdown-menu-right", a
});

538
src/main/resources/static/assets/datatables-buttons-2.4.1/js/buttons.html5.min.js
File diff suppressed because it is too large
View File

79
src/main/resources/static/assets/datatables-buttons-2.4.1/js/buttons.print.min.js

@ -0,0 +1,79 @@
/*!
* Print button for Buttons and DataTables.
* © SpryMedia Ltd - datatables.net/license
*/
! function(n) {
var o, r;
"function" == typeof define && define.amd ? define(["jquery", "datatables.net", "datatables.net-buttons"], function(t) {
return n(t, window, document)
}) : "object" == typeof exports ? (o = require("jquery"), r = function(t, e) {
e.fn.dataTable || require("datatables.net")(t, e), e.fn.dataTable.Buttons || require("datatables.net-buttons")(t, e)
}, "undefined" == typeof window ? module.exports = function(t, e) {
return t = t || window, e = e || o(t), r(t, e), n(e, t, t.document)
} : (r(window, o), module.exports = n(o, window, window.document))) : n(jQuery, window, document)
}(function(m, b, t, p) {
"use strict";
function h(t) {
return n.href = t, -1 === (t = n.host).indexOf("/") && 0 !== n.pathname.indexOf("/") && (t += "/"), n.protocol + "//" + t + n.pathname + n.search
}
var e = m.fn.dataTable,
n = t.createElement("a");
return e.ext.buttons.print = {
className: "buttons-print",
text: function(t) {
return t.i18n("buttons.print", "Print")
},
action: function(t, e, n, o) {
function r(t, e) {
for (var n = "<tr>", o = 0, r = t.length; o < r; o++) {
var i = null === t[o] || t[o] === p ? "" : t[o];
n += "<" + e + " " + (s[o] ? 'class="' + s[o] + '"' : "") + ">" + i + "</" + e + ">"
}
return n + "</tr>"
}
var i = e.buttons.exportData(m.extend({
decodeEntities: !1
}, o.exportOptions)),
a = e.buttons.exportInfo(o),
s = e.columns(o.exportOptions.columns).flatten().map(function(t) {
return e.settings()[0].aoColumns[e.column(t).index()].sClass
}).toArray(),
u = '<table class="' + e.table().node().className + '">';
o.header && (u += "<thead>" + r(i.header, "th") + "</thead>"), u += "<tbody>";
for (var d = 0, c = i.body.length; d < c; d++) u += r(i.body[d], "td");
u += "</tbody>", o.footer && i.footer && (u += "<tfoot>" + r(i.footer, "th") + "</tfoot>"), u += "</table>";
var l = b.open("", "");
if (l) {
l.document.close();
var f = "<title>" + a.title + "</title>";
m("style, link").each(function() {
f += function(t) {
t = m(t).clone()[0];
return "link" === t.nodeName.toLowerCase() && (t.href = h(t.href)), t.outerHTML
}(this)
});
try {
l.document.head.innerHTML = f
} catch (t) {
m(l.document.head).html(f)
}
l.document.body.innerHTML = "<h1>" + a.title + "</h1><div>" + (a.messageTop || "") + "</div>" + u + "<div>" + (a.messageBottom || "") + "</div>", m(l.document.body).addClass("dt-print-view"), m("img", l.document.body).each(function(t, e) {
e.setAttribute("src", h(e.getAttribute("src")))
}), o.customize && o.customize(l, o, e);
a = function() {
o.autoPrint && (l.print(), l.close())
};
navigator.userAgent.match(/Trident\/\d.\d/) ? a() : l.setTimeout(a, 1e3)
} else e.buttons.info(e.i18n("buttons.printErrorTitle", "Unable to open print view"), e.i18n("buttons.printErrorMsg", "Please allow popups in your browser for this site to be able to view the print view."), 5e3)
},
title: "*",
messageTop: "*",
messageBottom: "*",
exportOptions: {},
header: !0,
footer: !1,
autoPrint: !0,
customize: null
}, e
});

764
src/main/resources/static/assets/datatables-buttons-2.4.1/js/dataTables.buttons.min.js

@ -0,0 +1,764 @@
/*! Buttons for DataTables 2.4.1
* © SpryMedia Ltd - datatables.net/license
*/
! function(e) {
var o, i;
"function" == typeof define && define.amd ? define(["jquery", "datatables.net"], function(t) {
return e(t, window, document)
}) : "object" == typeof exports ? (o = require("jquery"), i = function(t, n) {
n.fn.dataTable || require("datatables.net")(t, n)
}, "undefined" == typeof window ? module.exports = function(t, n) {
return t = t || window, n = n || o(t), i(t, n), e(n, t, t.document)
} : (i(window, o), module.exports = e(o, window, window.document))) : e(jQuery, window, document)
}(function(y, v, x, w) {
"use strict";
var e = y.fn.dataTable,
o = 0,
C = 0,
_ = e.ext.buttons;
function A(t, n, e) {
y.fn.animate ? t.stop().fadeIn(n, e) : (t.css("display", "block"), e && e.call(t))
}
function k(t, n, e) {
y.fn.animate ? t.stop().fadeOut(n, e) : (t.css("display", "none"), e && e.call(t))
}
function B(n, t) {
if (!(this instanceof B)) return function(t) {
return new B(t, n).container()
};
!0 === (t = void 0 === t ? {} : t) && (t = {}), Array.isArray(t) && (t = {
buttons: t
}), this.c = y.extend(!0, {}, B.defaults, t), t.buttons && (this.c.buttons = t.buttons), this.s = {
dt: new e.Api(n),
buttons: [],
listenKeys: "",
namespace: "dtb" + o++
}, this.dom = {
container: y("<" + this.c.dom.container.tag + "/>").addClass(this.c.dom.container.className)
}, this._constructor()
}
y.extend(B.prototype, {
action: function(t, n) {
t = this._nodeToButton(t);
return n === w ? t.conf.action : (t.conf.action = n, this)
},
active: function(t, n) {
var t = this._nodeToButton(t),
e = this.c.dom.button.active,
o = y(t.node);
return t.inCollection && this.c.dom.collection.button && this.c.dom.collection.button.active !== w && (e = this.c.dom.collection.button.active), n === w ? o.hasClass(e) : (o.toggleClass(e, n === w || n), this)
},
add: function(t, n, e) {
var o = this.s.buttons;
if ("string" == typeof n) {
for (var i = n.split("-"), s = this.s, a = 0, r = i.length - 1; a < r; a++) s = s.buttons[+i[a]];
o = s.buttons, n = +i[i.length - 1]
}
return this._expandButton(o, t, t !== w ? t.split : w, (t === w || t.split === w || 0 === t.split.length) && s !== w, !1, n), e !== w && !0 !== e || this._draw(), this
},
collectionRebuild: function(t, n) {
var e = this._nodeToButton(t);
if (n !== w) {
for (var o = e.buttons.length - 1; 0 <= o; o--) this.remove(e.buttons[o].node);
for (e.conf.prefixButtons && n.unshift.apply(n, e.conf.prefixButtons), e.conf.postfixButtons && n.push.apply(n, e.conf.postfixButtons), o = 0; o < n.length; o++) {
var i = n[o];
this._expandButton(e.buttons, i, i !== w && i.config !== w && i.config.split !== w, !0, i.parentConf !== w && i.parentConf.split !== w, null, i.parentConf)
}
}
this._draw(e.collection, e.buttons)
},
container: function() {
return this.dom.container
},
disable: function(t) {
t = this._nodeToButton(t);
return y(t.node).addClass(this.c.dom.button.disabled).prop("disabled", !0), this
},
destroy: function() {
y("body").off("keyup." + this.s.namespace);
for (var t = this.s.buttons.slice(), n = 0, e = t.length; n < e; n++) this.remove(t[n].node);
this.dom.container.remove();
var o = this.s.dt.settings()[0];
for (n = 0, e = o.length; n < e; n++)
if (o.inst === this) {
o.splice(n, 1);
break
} return this
},
enable: function(t, n) {
return !1 === n ? this.disable(t) : (n = this._nodeToButton(t), y(n.node).removeClass(this.c.dom.button.disabled).prop("disabled", !1), this)
},
index: function(t, n, e) {
n || (n = "", e = this.s.buttons);
for (var o = 0, i = e.length; o < i; o++) {
var s = e[o].buttons;
if (e[o].node === t) return n + o;
if (s && s.length) {
s = this.index(t, o + "-", s);
if (null !== s) return s
}
}
return null
},
name: function() {
return this.c.name
},
node: function(t) {
return t ? (t = this._nodeToButton(t), y(t.node)) : this.dom.container
},
processing: function(t, n) {
var e = this.s.dt,
o = this._nodeToButton(t);
return n === w ? y(o.node).hasClass("processing") : (y(o.node).toggleClass("processing", n), y(e.table().node()).triggerHandler("buttons-processing.dt", [n, e.button(t), e, y(t), o.conf]), this)
},
remove: function(t) {
var n = this._nodeToButton(t),
e = this._nodeToHost(t),
o = this.s.dt;
if (n.buttons.length)
for (var i = n.buttons.length - 1; 0 <= i; i--) this.remove(n.buttons[i].node);
n.conf.destroying = !0, n.conf.destroy && n.conf.destroy.call(o.button(t), o, y(t), n.conf), this._removeKey(n.conf), y(n.node).remove();
o = y.inArray(n, e);
return e.splice(o, 1), this
},
text: function(t, n) {
function e(t) {
return "function" == typeof t ? t(i, s, o.conf) : t
}
var o = this._nodeToButton(t),
t = o.textNode,
i = this.s.dt,
s = y(o.node);
return n === w ? e(o.conf.text) : (o.conf.text = n, t.html(e(n)), this)
},
_constructor: function() {
var e = this,
t = this.s.dt,
o = t.settings()[0],
n = this.c.buttons;
o._buttons || (o._buttons = []), o._buttons.push({
inst: this,
name: this.c.name
});
for (var i = 0, s = n.length; i < s; i++) this.add(n[i]);
t.on("destroy", function(t, n) {
n === o && e.destroy()
}), y("body").on("keyup." + this.s.namespace, function(t) {
var n;
x.activeElement && x.activeElement !== x.body || (n = String.fromCharCode(t.keyCode).toLowerCase(), -1 !== e.s.listenKeys.toLowerCase().indexOf(n) && e._keypress(n, t))
})
},
_addKey: function(t) {
t.key && (this.s.listenKeys += (y.isPlainObject(t.key) ? t.key : t).key)
},
_draw: function(t, n) {
t || (t = this.dom.container, n = this.s.buttons), t.children().detach();
for (var e = 0, o = n.length; e < o; e++) t.append(n[e].inserter), t.append(" "), n[e].buttons && n[e].buttons.length && this._draw(n[e].collection, n[e].buttons)
},
_expandButton: function(t, n, e, o, i, s, a) {
for (var r, l = this.s.dt, c = this.c.dom.collection, u = Array.isArray(n) ? n : [n], d = 0, f = (u = n === w ? Array.isArray(e) ? e : [e] : u).length; d < f; d++) {
var p = this._resolveExtends(u[d]);
if (p)
if (r = !(!p.config || !p.config.split), Array.isArray(p)) this._expandButton(t, p, h !== w && h.conf !== w ? h.conf.split : w, o, a !== w && a.split !== w, s, a);
else {
var h = this._buildButton(p, o, p.split !== w || p.config !== w && p.config.split !== w, i);
if (h) {
if (s !== w && null !== s ? (t.splice(s, 0, h), s++) : t.push(h), h.conf.buttons && (h.collection = y("<" + c.container.content.tag + "/>"), h.conf._collection = h.collection, y(h.node).append(c.action.dropHtml), this._expandButton(h.buttons, h.conf.buttons, h.conf.split, !r, r, s, h.conf)), h.conf.split) {
h.collection = y("<" + c.container.tag + "/>"), h.conf._collection = h.collection;
for (var b = 0; b < h.conf.split.length; b++) {
var g = h.conf.split[b];
"object" == typeof g && (g.parent = a, g.collectionLayout === w && (g.collectionLayout = h.conf.collectionLayout), g.dropup === w && (g.dropup = h.conf.dropup), g.fade === w) && (g.fade = h.conf.fade)
}
this._expandButton(h.buttons, h.conf.buttons, h.conf.split, !r, r, s, h.conf)
}
h.conf.parent = a, p.init && p.init.call(l.button(h.node), l, y(h.node), p)
}
}
}
},
_buildButton: function(n, t, e, o) {
function i(t) {
return "function" == typeof t ? t(u, l, n) : t
}
var s, a, r, l, c = this.c.dom,
u = this.s.dt,
d = y.extend(!0, {}, c.button);
if (t && e && c.collection.split ? y.extend(!0, d, c.collection.split.action) : o || t ? y.extend(!0, d, c.collection.button) : e && y.extend(!0, d, c.split.button), n.spacer) return c = y("<" + d.spacer.tag + "/>").addClass("dt-button-spacer " + n.style + " " + d.spacer.className).html(i(n.text)), {
conf: n,
node: c,
inserter: c,
buttons: [],
inCollection: t,
isSplit: e,
collection: null,
textNode: c
};
if (n.available && !n.available(u, n) && !n.hasOwnProperty("html")) return !1;
n.hasOwnProperty("html") ? l = y(n.html) : (a = function(t, n, e, o) {
o.action.call(n.button(e), t, n, e, o), y(n.table().node()).triggerHandler("buttons-action.dt", [n.button(e), n, e, o])
}, c = n.tag || d.tag, r = n.clickBlurs === w || n.clickBlurs, l = y("<" + c + "/>").addClass(d.className).attr("tabindex", this.s.dt.settings()[0].iTabIndex).attr("aria-controls", this.s.dt.table().node().id).on("click.dtb", function(t) {
t.preventDefault(), !l.hasClass(d.disabled) && n.action && a(t, u, l, n), r && l.trigger("blur")
}).on("keypress.dtb", function(t) {
13 === t.keyCode && (t.preventDefault(), !l.hasClass(d.disabled)) && n.action && a(t, u, l, n)
}), "a" === c.toLowerCase() && l.attr("href", "#"), "button" === c.toLowerCase() && l.attr("type", "button"), s = d.liner.tag ? (c = y("<" + d.liner.tag + "/>").html(i(n.text)).addClass(d.liner.className), "a" === d.liner.tag.toLowerCase() && c.attr("href", "#"), l.append(c), c) : (l.html(i(n.text)), l), !1 === n.enabled && l.addClass(d.disabled), n.className && l.addClass(n.className), n.titleAttr && l.attr("title", i(n.titleAttr)), n.attr && l.attr(n.attr), n.namespace || (n.namespace = ".dt-button-" + C++), n.config !== w && n.config.split && (n.split = n.config.split));
var f, p, h, b, g, m, c = this.c.dom.buttonContainer,
c = c && c.tag ? y("<" + c.tag + "/>").addClass(c.className).append(l) : l;
return this._addKey(n), this.c.buttonCreated && (c = this.c.buttonCreated(n, c)), e && (p = (f = t ? y.extend(!0, this.c.dom.split, this.c.dom.collection.split) : this.c.dom.split).wrapper, h = y("<" + p.tag + "/>").addClass(p.className).append(l), b = y.extend(n, {
align: f.dropdown.align,
attr: {
"aria-haspopup": "dialog",
"aria-expanded": !1
},
className: f.dropdown.className,
closeButton: !1,
splitAlignClass: f.dropdown.splitAlignClass,
text: f.dropdown.text
}), this._addKey(b), g = function(t, n, e, o) {
_.split.action.call(n.button(h), t, n, e, o), y(n.table().node()).triggerHandler("buttons-action.dt", [n.button(e), n, e, o]), e.attr("aria-expanded", !0)
}, m = y('<button class="' + f.dropdown.className + ' dt-button"></button>').html(f.dropdown.dropHtml).on("click.dtb", function(t) {
t.preventDefault(), t.stopPropagation(), m.hasClass(d.disabled) || g(t, u, m, b), r && m.trigger("blur")
}).on("keypress.dtb", function(t) {
13 === t.keyCode && (t.preventDefault(), m.hasClass(d.disabled) || g(t, u, m, b))
}), 0 === n.split.length && m.addClass("dtb-hide-drop"), h.append(m).attr(b.attr)), {
conf: n,
node: (e ? h : l).get(0),
inserter: e ? h : c,
buttons: [],
inCollection: t,
isSplit: e,
inSplit: o,
collection: null,
textNode: s
}
},
_nodeToButton: function(t, n) {
for (var e = 0, o = (n = n || this.s.buttons).length; e < o; e++) {
if (n[e].node === t) return n[e];
if (n[e].buttons.length) {
var i = this._nodeToButton(t, n[e].buttons);
if (i) return i
}
}
},
_nodeToHost: function(t, n) {
for (var e = 0, o = (n = n || this.s.buttons).length; e < o; e++) {
if (n[e].node === t) return n;
if (n[e].buttons.length) {
var i = this._nodeToHost(t, n[e].buttons);
if (i) return i
}
}
},
_keypress: function(s, a) {
var r;
a._buttonsHandled || (r = function(t) {
for (var n, e, o = 0, i = t.length; o < i; o++) n = t[o].conf, e = t[o].node, !n.key || n.key !== s && (!y.isPlainObject(n.key) || n.key.key !== s || n.key.shiftKey && !a.shiftKey || n.key.altKey && !a.altKey || n.key.ctrlKey && !a.ctrlKey || n.key.metaKey && !a.metaKey) || (a._buttonsHandled = !0, y(e).click()), t[o].buttons.length && r(t[o].buttons)
})(this.s.buttons)
},
_removeKey: function(t) {
var n;
t.key && (t = (y.isPlainObject(t.key) ? t.key : t).key, n = this.s.listenKeys.split(""), t = y.inArray(t, n), n.splice(t, 1), this.s.listenKeys = n.join(""))
},
_resolveExtends: function(e) {
function t(t) {
for (var n = 0; !y.isPlainObject(t) && !Array.isArray(t);) {
if (t === w) return;
if ("function" == typeof t) {
if (!(t = t.call(i, s, e))) return !1
} else if ("string" == typeof t) {
if (!_[t]) return {
html: t
};
t = _[t]
}
if (30 < ++n) throw "Buttons: Too many iterations"
}
return Array.isArray(t) ? t : y.extend({}, t)
}
var n, o, i = this,
s = this.s.dt;
for (e = t(e); e && e.extend;) {
if (!_[e.extend]) throw "Cannot extend unknown button type: " + e.extend;
var a = t(_[e.extend]);
if (Array.isArray(a)) return a;
if (!a) return !1;
var r = a.className;
e.config !== w && a.config !== w && (e.config = y.extend({}, a.config, e.config)), e = y.extend({}, a, e), r && e.className !== r && (e.className = r + " " + e.className), e.extend = a.extend
}
var l = e.postfixButtons;
if (l)
for (e.buttons || (e.buttons = []), n = 0, o = l.length; n < o; n++) e.buttons.push(l[n]);
var c = e.prefixButtons;
if (c)
for (e.buttons || (e.buttons = []), n = 0, o = c.length; n < o; n++) e.buttons.splice(n, 0, c[n]);
return e
},
_popover: function(o, t, n, e) {
function i() {
h = !0, k(y(g), b.fade, function() {
y(this).detach()
}), y(f.buttons('[aria-haspopup="dialog"][aria-expanded="true"]').nodes()).attr("aria-expanded", "false"), y("div.dt-button-background").off("click.dtb-collection"), B.background(!1, b.backgroundClassName, b.fade, m), y(v).off("resize.resize.dtb-collection"), y("body").off(".dtb-collection"), f.off("buttons-action.b-internal"), f.off("destroy")
}
var s, a, r, l, c, u, d, f = t,
p = this.c,
h = !1,
b = y.extend({
align: "button-left",
autoClose: !1,
background: !0,
backgroundClassName: "dt-button-background",
closeButton: !0,
containerClassName: p.dom.collection.container.className,
contentClassName: p.dom.collection.container.content.className,
collectionLayout: "",
collectionTitle: "",
dropup: !1,
fade: 400,
popoverTitle: "",
rightAlignClassName: "dt-button-right",
tag: p.dom.collection.container.tag
}, n),
g = b.tag + "." + b.containerClassName.replace(/ /g, "."),
m = t.node();
!1 === o ? i() : ((p = y(f.buttons('[aria-haspopup="dialog"][aria-expanded="true"]').nodes())).length && (m.closest(g).length && (m = p.eq(0)), i()), n = y(".dt-button", o).length, p = "", 3 === n ? p = "dtb-b3" : 2 === n ? p = "dtb-b2" : 1 === n && (p = "dtb-b1"), s = y("<" + b.tag + "/>").addClass(b.containerClassName).addClass(b.collectionLayout).addClass(b.splitAlignClass).addClass(p).css("display", "none").attr({
"aria-modal": !0,
role: "dialog"
}), o = y(o).addClass(b.contentClassName).attr("role", "menu").appendTo(s), m.attr("aria-expanded", "true"), m.parents("body")[0] !== x.body && (m = x.body.lastChild), b.popoverTitle ? s.prepend('<div class="dt-button-collection-title">' + b.popoverTitle + "</div>") : b.collectionTitle && s.prepend('<div class="dt-button-collection-title">' + b.collectionTitle + "</div>"), b.closeButton && s.prepend('<div class="dtb-popover-close">&times;</div>').addClass("dtb-collection-closeable"), A(s.insertAfter(m), b.fade), n = y(t.table().container()), d = s.css("position"), "container" !== b.span && "dt-container" !== b.align || (m = m.parent(), s.css("width", n.width())), "absolute" === d ? (p = y(m[0].offsetParent), t = m.position(), n = m.offset(), a = p.offset(), r = p.position(), l = v.getComputedStyle(p[0]), a.height = p.outerHeight(), a.width = p.width() + parseFloat(l.paddingLeft), a.right = a.left + a.width, a.bottom = a.top + a.height, p = t.top + m.outerHeight(), c = t.left, s.css({
top: p,
left: c
}), l = v.getComputedStyle(s[0]), (u = s.offset()).height = s.outerHeight(), u.width = s.outerWidth(), u.right = u.left + u.width, u.bottom = u.top + u.height, u.marginTop = parseFloat(l.marginTop), u.marginBottom = parseFloat(l.marginBottom), b.dropup && (p = t.top - u.height - u.marginTop - u.marginBottom), "button-right" !== b.align && !s.hasClass(b.rightAlignClassName) || (c = t.left - u.width + m.outerWidth()), "dt-container" !== b.align && "container" !== b.align || (c = c < t.left ? -t.left : c) + u.width > a.width && (c = a.width - u.width), r.left + c + u.width > y(v).width() && (c = y(v).width() - u.width - r.left), n.left + c < 0 && (c = -n.left), r.top + p + u.height > y(v).height() + y(v).scrollTop() && (p = t.top - u.height - u.marginTop - u.marginBottom), r.top + p < y(v).scrollTop() && (p = t.top + m.outerHeight()), s.css({
top: p,
left: c
})) : ((d = function() {
var t = y(v).height() / 2,
n = s.height() / 2;
s.css("marginTop", -1 * (n = t < n ? t : n))
})(), y(v).on("resize.dtb-collection", function() {
d()
})), b.background && B.background(!0, b.backgroundClassName, b.fade, b.backgroundHost || m), y("div.dt-button-background").on("click.dtb-collection", function() { }), b.autoClose && setTimeout(function() {
f.on("buttons-action.b-internal", function(t, n, e, o) {
o[0] !== m[0] && i()
})
}, 0), y(s).trigger("buttons-popover.dt"), f.on("destroy", i), setTimeout(function() {
h = !1, y("body").on("click.dtb-collection", function(t) {
var n, e;
!h && (n = y.fn.addBack ? "addBack" : "andSelf", e = y(t.target).parent()[0], !y(t.target).parents()[n]().filter(o).length && !y(e).hasClass("dt-buttons") || y(t.target).hasClass("dt-button-background")) && i()
}).on("keyup.dtb-collection", function(t) {
27 === t.keyCode && i()
}).on("keydown.dtb-collection", function(t) {
var n = y("a, button", o),
e = x.activeElement;
9 === t.keyCode && (-1 === n.index(e) ? (n.first().focus(), t.preventDefault()) : t.shiftKey ? e === n[0] && (n.last().focus(), t.preventDefault()) : e === n.last()[0] && (n.first().focus(), t.preventDefault()))
})
}, 0))
}
}), B.background = function(t, n, e, o) {
e === w && (e = 400), o = o || x.body, t ? A(y("<div/>").addClass(n).css("display", "none").insertAfter(o), e) : k(y("div." + n), e, function() {
y(this).removeClass(n).remove()
})
}, B.instanceSelector = function(t, i) {
var s, a, r;
return t === w || null === t ? y.map(i, function(t) {
return t.inst
}) : (s = [], a = y.map(i, function(t) {
return t.name
}), (r = function(t) {
var n;
if (Array.isArray(t))
for (var e = 0, o = t.length; e < o; e++) r(t[e]);
else "string" == typeof t ? -1 !== t.indexOf(",") ? r(t.split(",")) : -1 !== (n = y.inArray(t.trim(), a)) && s.push(i[n].inst) : "number" == typeof t ? s.push(i[t].inst) : "object" == typeof t && s.push(t)
})(t), s)
}, B.buttonSelector = function(t, n) {
for (var c = [], u = function(t, n, e) {
for (var o, i, s = 0, a = n.length; s < a; s++)(o = n[s]) && (t.push({
node: o.node,
name: o.conf.name,
idx: i = e !== w ? e + s : s + ""
}), o.buttons) && u(t, o.buttons, i + "-")
}, d = function(t, n) {
var e = [],
o = (u(e, n.s.buttons), y.map(e, function(t) {
return t.node
}));
if (Array.isArray(t) || t instanceof y)
for (s = 0, a = t.length; s < a; s++) d(t[s], n);
else if (null === t || t === w || "*" === t)
for (s = 0, a = e.length; s < a; s++) c.push({
inst: n,
node: e[s].node
});
else if ("number" == typeof t) n.s.buttons[t] && c.push({
inst: n,
node: n.s.buttons[t].node
});
else if ("string" == typeof t)
if (-1 !== t.indexOf(","))
for (var i = t.split(","), s = 0, a = i.length; s < a; s++) d(i[s].trim(), n);
else if (t.match(/^\d+(\-\d+)*$/)) {
var r = y.map(e, function(t) {
return t.idx
});
c.push({
inst: n,
node: e[y.inArray(t, r)].node
})
} else if (-1 !== t.indexOf(":name")) {
var l = t.replace(":name", "");
for (s = 0, a = e.length; s < a; s++) e[s].name === l && c.push({
inst: n,
node: e[s].node
})
} else y(o).filter(t).each(function() {
c.push({
inst: n,
node: this
})
});
else "object" == typeof t && t.nodeName && -1 !== (r = y.inArray(t, o)) && c.push({
inst: n,
node: o[r]
})
}, e = 0, o = t.length; e < o; e++) {
var i = t[e];
d(n, i)
}
return c
}, B.stripData = function(t, n) {
return "string" == typeof t && (t = (t = t.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "")).replace(/<!\-\-.*?\-\->/g, ""), n && !n.stripHtml || (t = t.replace(/<[^>]*>/g, "")), n && !n.trim || (t = t.replace(/^\s+|\s+$/g, "")), n && !n.stripNewlines || (t = t.replace(/\n/g, " ")), !n || n.decodeEntities) && (l.innerHTML = t, t = l.value), t
}, B.defaults = {
buttons: ["copy", "excel", "csv", "pdf", "print"],
name: "main",
tabIndex: 0,
dom: {
container: {
tag: "div",
className: "dt-buttons"
},
collection: {
action: {
dropHtml: '<span class="dt-button-down-arrow">&#x25BC;</span>'
},
container: {
className: "dt-button-collection",
content: {
className: "",
tag: "div"
},
tag: "div"
}
},
button: {
tag: "button",
className: "dt-button",
active: "dt-button-active",
disabled: "disabled",
spacer: {
className: "dt-button-spacer",
tag: "span"
},
liner: {
tag: "span",
className: ""
}
},
split: {
action: {
className: "dt-button-split-drop-button dt-button",
tag: "button"
},
dropdown: {
align: "split-right",
className: "dt-button-split-drop",
dropHtml: '<span class="dt-button-down-arrow">&#x25BC;</span>',
splitAlignClass: "dt-button-split-left",
tag: "button"
},
wrapper: {
className: "dt-button-split",
tag: "div"
}
}
}
}, y.extend(_, {
collection: {
text: function(t) {
return t.i18n("buttons.collection", "Collection")
},
className: "buttons-collection",
closeButton: !(B.version = "2.4.1"),
init: function(t, n, e) {
n.attr("aria-expanded", !1)
},
action: function(t, n, e, o) {
o._collection.parents("body").length ? this.popover(!1, o) : this.popover(o._collection, o), "keypress" === t.type && y("a, button", o._collection).eq(0).focus()
},
attr: {
"aria-haspopup": "dialog"
}
},
split: {
text: function(t) {
return t.i18n("buttons.split", "Split")
},
className: "buttons-split",
closeButton: !1,
init: function(t, n, e) {
return n.attr("aria-expanded", !1)
},
action: function(t, n, e, o) {
this.popover(o._collection, o)
},
attr: {
"aria-haspopup": "dialog"
}
},
copy: function(t, n) {
if (_.copyHtml5) return "copyHtml5"
},
csv: function(t, n) {
if (_.csvHtml5 && _.csvHtml5.available(t, n)) return "csvHtml5"
},
excel: function(t, n) {
if (_.excelHtml5 && _.excelHtml5.available(t, n)) return "excelHtml5"
},
pdf: function(t, n) {
if (_.pdfHtml5 && _.pdfHtml5.available(t, n)) return "pdfHtml5"
},
pageLength: function(t) {
var n = t.settings()[0].aLengthMenu,
e = [],
o = [];
if (Array.isArray(n[0])) e = n[0], o = n[1];
else
for (var i = 0; i < n.length; i++) {
var s = n[i];
y.isPlainObject(s) ? (e.push(s.value), o.push(s.label)) : (e.push(s), o.push(s))
}
return {
extend: "collection",
text: function(t) {
return t.i18n("buttons.pageLength", {
"-1": "Show all rows",
_: "Show %d rows"
}, t.page.len())
},
className: "buttons-page-length",
autoClose: !0,
buttons: y.map(e, function(s, t) {
return {
text: o[t],
className: "button-page-length",
action: function(t, n) {
n.page.len(s).draw()
},
init: function(t, n, e) {
function o() {
i.active(t.page.len() === s)
}
var i = this;
t.on("length.dt" + e.namespace, o), o()
},
destroy: function(t, n, e) {
t.off("length.dt" + e.namespace)
}
}
}),
init: function(t, n, e) {
var o = this;
t.on("length.dt" + e.namespace, function() {
o.text(e.text)
})
},
destroy: function(t, n, e) {
t.off("length.dt" + e.namespace)
}
}
},
spacer: {
style: "empty",
spacer: !0,
text: function(t) {
return t.i18n("buttons.spacer", "")
}
}
}), e.Api.register("buttons()", function(n, e) {
e === w && (e = n, n = w), this.selector.buttonGroup = n;
var t = this.iterator(!0, "table", function(t) {
if (t._buttons) return B.buttonSelector(B.instanceSelector(n, t._buttons), e)
}, !0);
return t._groupSelector = n, t
}), e.Api.register("button()", function(t, n) {
t = this.buttons(t, n);
return 1 < t.length && t.splice(1, t.length), t
}), e.Api.registerPlural("buttons().active()", "button().active()", function(n) {
return n === w ? this.map(function(t) {
return t.inst.active(t.node)
}) : this.each(function(t) {
t.inst.active(t.node, n)
})
}), e.Api.registerPlural("buttons().action()", "button().action()", function(n) {
return n === w ? this.map(function(t) {
return t.inst.action(t.node)
}) : this.each(function(t) {
t.inst.action(t.node, n)
})
}), e.Api.registerPlural("buttons().collectionRebuild()", "button().collectionRebuild()", function(e) {
return this.each(function(t) {
for (var n = 0; n < e.length; n++) "object" == typeof e[n] && (e[n].parentConf = t);
t.inst.collectionRebuild(t.node, e)
})
}), e.Api.register(["buttons().enable()", "button().enable()"], function(n) {
return this.each(function(t) {
t.inst.enable(t.node, n)
})
}), e.Api.register(["buttons().disable()", "button().disable()"], function() {
return this.each(function(t) {
t.inst.disable(t.node)
})
}), e.Api.register("button().index()", function() {
var n = null;
return this.each(function(t) {
t = t.inst.index(t.node);
null !== t && (n = t)
}), n
}), e.Api.registerPlural("buttons().nodes()", "button().node()", function() {
var n = y();
return y(this.each(function(t) {
n = n.add(t.inst.node(t.node))
})), n
}), e.Api.registerPlural("buttons().processing()", "button().processing()", function(n) {
return n === w ? this.map(function(t) {
return t.inst.processing(t.node)
}) : this.each(function(t) {
t.inst.processing(t.node, n)
})
}), e.Api.registerPlural("buttons().text()", "button().text()", function(n) {
return n === w ? this.map(function(t) {
return t.inst.text(t.node)
}) : this.each(function(t) {
t.inst.text(t.node, n)
})
}), e.Api.registerPlural("buttons().trigger()", "button().trigger()", function() {
return this.each(function(t) {
t.inst.node(t.node).trigger("click")
})
}), e.Api.register("button().popover()", function(n, e) {
return this.map(function(t) {
return t.inst._popover(n, this.button(this[0].node), e)
})
}), e.Api.register("buttons().containers()", function() {
var i = y(),
s = this._groupSelector;
return this.iterator(!0, "table", function(t) {
if (t._buttons)
for (var n = B.instanceSelector(s, t._buttons), e = 0, o = n.length; e < o; e++) i = i.add(n[e].container())
}), i
}), e.Api.register("buttons().container()", function() {
return this.containers().eq(0)
}), e.Api.register("button().add()", function(t, n, e) {
var o = this.context;
return o.length && (o = B.instanceSelector(this._groupSelector, o[0]._buttons)).length && o[0].add(n, t, e), this.button(this._groupSelector, t)
}), e.Api.register("buttons().destroy()", function() {
return this.pluck("inst").unique().each(function(t) {
t.destroy()
}), this
}), e.Api.registerPlural("buttons().remove()", "buttons().remove()", function() {
return this.each(function(t) {
t.inst.remove(t.node)
}), this
}), e.Api.register("buttons.info()", function(t, n, e) {
var o = this;
return !1 === t ? (this.off("destroy.btn-info"), k(y("#datatables_buttons_info"), 400, function() {
y(this).remove()
}), clearTimeout(i), i = null) : (i && clearTimeout(i), y("#datatables_buttons_info").length && y("#datatables_buttons_info").remove(), t = t ? "<h2>" + t + "</h2>" : "", A(y('<div id="datatables_buttons_info" class="dt-button-info"/>').html(t).append(y("<div/>")["string" == typeof n ? "html" : "append"](n)).css("display", "none").appendTo("body")), e !== w && 0 !== e && (i = setTimeout(function() {
o.buttons.info(!1)
}, e)), this.on("destroy.btn-info", function() {
o.buttons.info(!1)
})), this
}), e.Api.register("buttons.exportData()", function(t) {
if (this.context.length) return c(new e.Api(this.context[0]), t)
}), e.Api.register("buttons.exportInfo()", function(t) {
return {
filename: n(t = t || {}),
title: a(t),
messageTop: r(this, t.message || t.messageTop, "top"),
messageBottom: r(this, t.messageBottom, "bottom")
}
});
var i, n = function(t) {
var n;
return (n = "function" == typeof (n = "*" === t.filename && "*" !== t.title && t.title !== w && null !== t.title && "" !== t.title ? t.title : t.filename) ? n() : n) === w || null === n ? null : (n = (n = -1 !== n.indexOf("*") ? n.replace("*", y("head > title").text()).trim() : n).replace(/[^a-zA-Z0-9_\u00A1-\uFFFF\.,\-_ !\(\)]/g, "")) + (s(t.extension) || "")
},
s = function(t) {
return null === t || t === w ? null : "function" == typeof t ? t() : t
},
a = function(t) {
t = s(t.title);
return null === t ? null : -1 !== t.indexOf("*") ? t.replace("*", y("head > title").text() || "Exported data") : t
},
r = function(t, n, e) {
n = s(n);
return null === n ? null : (t = y("caption", t.table().container()).eq(0), "*" === n ? t.css("caption-side") !== e ? null : t.length ? t.text() : "" : n)
},
l = y("<textarea/>")[0],
c = function(e, t) {
for (var o = y.extend(!0, {}, {
rows: null,
columns: "",
modifier: {
search: "applied",
order: "applied"
},
orthogonal: "display",
stripHtml: !0,
stripNewlines: !0,
decodeEntities: !0,
trim: !0,
format: {
header: function(t) {
return B.stripData(t, o)
},
footer: function(t) {
return B.stripData(t, o)
},
body: function(t) {
return B.stripData(t, o)
}
},
customizeData: null
}, t), t = e.columns(o.columns).indexes().map(function(t) {
var n = e.column(t).header();
return o.format.header(n.innerHTML, t, n)
}).toArray(), n = e.table().footer() ? e.columns(o.columns).indexes().map(function(t) {
var n = e.column(t).footer();
return o.format.footer(n ? n.innerHTML : "", t, n)
}).toArray() : null, i = y.extend({}, o.modifier), i = (e.select && "function" == typeof e.select.info && i.selected === w && e.rows(o.rows, y.extend({
selected: !0
}, i)).any() && y.extend(i, {
selected: !0
}), e.rows(o.rows, i).indexes().toArray()), i = e.cells(i, o.columns), s = i.render(o.orthogonal).toArray(), a = i.nodes().toArray(), r = t.length, l = [], c = 0, u = 0, d = 0 < r ? s.length / r : 0; u < d; u++) {
for (var f = [r], p = 0; p < r; p++) f[p] = o.format.body(s[c], u, p, a[c]), c++;
l[u] = f
}
i = {
header: t,
footer: n,
body: l
};
return o.customizeData && o.customizeData(i), i
};
function t(t, n) {
t = new e.Api(t), n = n || t.init().buttons || e.defaults.buttons;
return new B(t, n).container()
}
return y.fn.dataTable.Buttons = B, y.fn.DataTable.Buttons = B, y(x).on("init.dt plugin-init.dt", function(t, n) {
"dt" === t.namespace && (t = n.oInit.buttons || e.defaults.buttons) && !n._buttons && new B(n, t).container()
}), e.ext.feature.push({
fnInit: t,
cFeature: "B"
}), e.ext.features && e.ext.features.register("buttons", t), e
});

6995
src/main/resources/static/assets/font-awesome-6.5.1/css/all.min.css
File diff suppressed because it is too large
View File

BIN
src/main/resources/static/assets/font-awesome-6.5.1/webfonts/fa-brands-400.ttf

BIN
src/main/resources/static/assets/font-awesome-6.5.1/webfonts/fa-brands-400.woff2

BIN
src/main/resources/static/assets/font-awesome-6.5.1/webfonts/fa-regular-400.ttf

BIN
src/main/resources/static/assets/font-awesome-6.5.1/webfonts/fa-regular-400.woff2

BIN
src/main/resources/static/assets/font-awesome-6.5.1/webfonts/fa-solid-900.ttf

BIN
src/main/resources/static/assets/font-awesome-6.5.1/webfonts/fa-solid-900.woff2

BIN
src/main/resources/static/assets/font-awesome-6.5.1/webfonts/fa-v4compatibility.ttf

BIN
src/main/resources/static/assets/font-awesome-6.5.1/webfonts/fa-v4compatibility.woff2

7023
src/main/resources/static/assets/font-awesome-6.5.2/css/all.min.css
File diff suppressed because it is too large
View File

BIN
src/main/resources/static/assets/font-awesome-6.5.2/webfonts/fa-brands-400.ttf

BIN
src/main/resources/static/assets/font-awesome-6.5.2/webfonts/fa-brands-400.woff2

BIN
src/main/resources/static/assets/font-awesome-6.5.2/webfonts/fa-regular-400.ttf

BIN
src/main/resources/static/assets/font-awesome-6.5.2/webfonts/fa-regular-400.woff2

BIN
src/main/resources/static/assets/font-awesome-6.5.2/webfonts/fa-solid-900.ttf

BIN
src/main/resources/static/assets/font-awesome-6.5.2/webfonts/fa-solid-900.woff2

BIN
src/main/resources/static/assets/font-awesome-6.5.2/webfonts/fa-v4compatibility.ttf

BIN
src/main/resources/static/assets/font-awesome-6.5.2/webfonts/fa-v4compatibility.woff2

BIN
src/main/resources/static/assets/header-images/gcc1.png

After

Width: 325  |  Height: 435  |  Size: 204 KiB

3244
src/main/resources/static/assets/jquery-3.6.4/js/jquery-3.6.4.min.js
File diff suppressed because it is too large
View File

93
src/main/resources/static/assets/js/add-url-data-script.js

@ -0,0 +1,93 @@
// Global flags
let otpSent = false;
let isAdmin = false;
async function loadRole() {
try {
const res = await fetch(`${contextPath}api/get-role`, {
method: "POST"
});
const data = await res.json();
if (!data.authenticated) {
isAdmin = false;
return;
}
isAdmin = data.isAdmin;
} catch (e) {
// swallow network errors
console.error("Error: ", error);
}
}
// Open the login modal
function openLoginModal() {
if (isAdmin) {
// For admin → directly go to the post-verification page
window.open(`${contextPath}view-login-det`, '_blank');
return;
}
const modal = document.getElementById("login-modal");
modal.classList.remove("hide");
modal.classList.add("show");
modal.style.display = "flex"; // make it visible immediately before animation
}
// Load role when page loads
document.addEventListener("DOMContentLoaded", () => {
loadRole();
});
document.addEventListener("DOMContentLoaded", function() {
const togglePass = document.getElementById("togglePass");
const urlUsrPassInput = document.getElementById("urlDataUsrPass");
const form = document.getElementById("frm-url-det");
// safety check
if (!togglePass || !urlUsrPassInput) {
return;
}
// if (!urlUsrPassInput) return;
// --- Toggle Password Visibility ---
togglePass.addEventListener("click", function() {
const isPassword = urlUsrPassInput.type === "password";
urlUsrPassInput.type = isPassword ? "text" : "password";
togglePass.classList.toggle("fa-eye");
togglePass.classList.toggle("fa-eye-slash");
});
// --- Form submission logic ---
form.addEventListener("submit", function(e) {
e.preventDefault();
const formData = new FormData(form);
fetch(form.getAttribute("action"), {
method: "POST",
body: formData
})
.then(response => {
if (!response.ok) {
throw new Error("Network response was not ok: " + response.statusText);
}
return response.json();
})
.then(data => {
if (data.resStat) {
alert("Success: " + data.res);
form.reset();
} else {
alert("Failed: " + data.res);
}
})
.catch(error => {
console.error("Error adding url:", error);
alert("Error adding url. Please try again.");
});
});
});

20
src/main/resources/static/assets/js/fetch-csrf-script.js

@ -0,0 +1,20 @@
const csrfToken = document.querySelector('meta[name="_csrf"]').content;
const csrfHeader = document.querySelector('meta[name="_csrf_header"]').content;
// Save original fetch
const originalFetch = window.fetch;
window.fetch = (url, options = {}) => {
// Default method is GET if not provided
const method = (options.method || "GET").toUpperCase();
// Only attach CSRF for unsafe methods
if (["POST", "PUT", "DELETE", "PATCH"].includes(method)) {
options.headers = {
...options.headers,
[csrfHeader]: csrfToken
};
}
return originalFetch(url, options);
};

3
src/main/resources/static/assets/js/go-back-script.js

@ -0,0 +1,3 @@
function goBack() {
window.history.back();
}

420
src/main/resources/static/assets/js/home-script.js

@ -0,0 +1,420 @@
// Global flags
let otpSent = false;
let isAdmin = false;
async function clearBackendOtpSession() {
try {
await fetch(`${contextPath}api/clr-otp-ses`, { method: "POST" });
} catch (e) {
console.error("Failed to clear backend OTP session", e);
}
}
async function reloadMobileDropdown() {
try {
const res = await fetch(`${contextPath}api/get-mob-num`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: "{}" // empty JSON payload
});
const mobNumInput = document.getElementById("mobNum");
mobNumInput.innerHTML = ""; // clear all old options
if (!res.ok) {
mobNumInput.innerHTML = '<option value="">-- Failed to load numbers --</option>';
console.error(`Failed to fetch mobile numbers: ${res.status} ${res.statusText}`);
return;
}
if (res.status === 204) {
// No data returned
mobNumInput.innerHTML = '<option value="">-- No mobile numbers available --</option>';
return;
}
const data = await res.json();
data.forEach(item => {
const opt = document.createElement("option");
opt.value = item.mobNumId; // new UUID
opt.textContent = item.mobNum; // masked number
mobNumInput.appendChild(opt);
});
} catch (err) {
console.error("Error loading mobile numbers:", err);
const mobNumInput = document.getElementById("mobNum");
mobNumInput.innerHTML = '<option value="">-- Failed to load numbers --</option>';
}
}
async function loadRole() {
try {
const res = await fetch(`${contextPath}api/get-role`, {
method: "POST"
});
const data = await res.json();
if (!data.authenticated) {
isAdmin = false;
return;
}
isAdmin = data.isAdmin;
} catch (e) {
// swallow network errors
console.error("Error: ", error);
}
}
async function loadMobNum() {
try {
const res = await fetch(`${contextPath}api/get-mob-num`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: "{}"
});
const mobSelect = document.getElementById("mobNum");
mobSelect.innerHTML = '<option value="">-- Select Mobile Number --</option>';
if (res.status === 204) {
// No data returned
mobSelect.innerHTML = '<option value="">-- No mobile numbers available --</option>';
return;
}
if (!res.ok) {
throw new Error(`Server error: ${res.status}`);
}
const data = await res.json();
data.forEach(item => {
const opt = document.createElement("option");
opt.value = item.mobNumId; // mobNumId for backend
opt.textContent = item.mobNum; // Masked number for display
mobSelect.appendChild(opt);
});
} catch (err) {
console.error("Failed to load mobile numbers", err);
const mobSelect = document.getElementById("mobNum");
mobSelect.innerHTML = '<option value="">-- Failed to load numbers --</option>';
}
}
// Open the login modal
function openLoginModal() {
if (isAdmin) {
// For admin → directly go to the post-verification page
window.open(`${contextPath}view-login-det`, '_blank');
return;
}
const modal = document.getElementById("login-modal");
modal.classList.remove("hide");
modal.classList.add("show");
modal.style.display = "flex"; // make it visible immediately before animation
// Load only when modal is opened
loadMobNum();
}
// Close the modal and reset its state
async function closeLoginModal() {
const modal = document.getElementById("login-modal");
const otpForm = document.getElementById("otp-form");
const otpSection = document.getElementById("otp-section");
const sendOtpBtn = document.getElementById("send-otp-btn");
const messageEl = document.getElementById("otp-message");
const mobInput = document.getElementById("mobNum");
const otpInput = document.getElementById("otp");
// Reset form and state
if (otpForm) otpForm.reset(); // Reset the form fields
if (otpSection) otpSection.style.display = "none"; // Hide OTP section
if (sendOtpBtn) {
sendOtpBtn.textContent = "Get OTP"; // Reset button text
sendOtpBtn.disabled = false; // Enable the button
}
if (messageEl) {
messageEl.textContent = ""; // Clear any message
messageEl.style.color = "#333"; // Reset the default message color
}
// Reset mobile number input and OTP input
if (mobInput) mobInput.value = '';
if (otpInput) otpInput.value = '';
// Reset JS flag
otpSent = false;
await clearBackendOtpSession();
// Animate and hide the modal
modal.classList.remove("show");
modal.classList.add("hide");
// Handle the animation and hide modal after animation ends
modal.addEventListener(
"animationend",
function handler() {
if (modal.classList.contains("hide")) {
modal.style.display = "none"; // Hide modal after animation
}
modal.removeEventListener("animationend", handler); // Clean up the event listener
}
);
}
// Close the modal when clicking outside of it
window.onclick = function(event) {
const modal = document.getElementById("login-modal");
if (event.target === modal) {
closeLoginModal(); // Also reset everything when clicking outside
}
};
// Load role when page loads
document.addEventListener("DOMContentLoaded", () => {
loadRole();
});
// OTP Form Submission Handling
document.addEventListener("DOMContentLoaded", () => {
const otpForm = document.getElementById("otp-form");
if (!otpForm) return;
const messageEl = document.getElementById("otp-message");
const otpSection = document.getElementById("otp-section");
const sendOtpBtn = document.getElementById("send-otp-btn");
const mobNumInput = document.getElementById("mobNum");
const otpInput = document.getElementById("otp");
otpForm.addEventListener("submit", async (e) => {
e.preventDefault();
messageEl.textContent = "";
messageEl.style.color = "inherit";
const mobId = mobNumInput.value; // mobNumId
const selectedText = mobNumInput.options[mobNumInput.selectedIndex]?.text;
if (!mobId) {
messageEl.textContent = "Please select a mobile number.";
messageEl.style.color = "red";
return;
}
sendOtpBtn.disabled = true;
try {
if (!otpSent) {
// === GET OTP PHASE ===
sendOtpBtn.textContent = "Sending...";
const formData = new FormData();
formData.append("mobNumId", mobId);
const res = await fetch(`${contextPath}api/get-otp`, {
method: "POST",
body: formData, // sends as multipart/form-data
});
const data = await res.json();
if (res.ok && data.status === "success") {
messageEl.textContent = data.message || "OTP sent successfully!";
messageEl.style.color = "green";
otpSection.style.display = "block";
sendOtpBtn.textContent = "Verify OTP";
otpSent = true;
} else {
// backend may respond with 400 or 500
messageEl.textContent = data.message || "Failed to send OTP.";
messageEl.style.color = "red";
sendOtpBtn.textContent = "Get OTP";
}
} else {
// === VERIFY OTP PHASE ===
const enteredOtp = otpInput.value.trim();
if (!enteredOtp) {
messageEl.textContent = "Please enter the OTP.";
messageEl.style.color = "red";
return;
}
sendOtpBtn.textContent = "Verifying...";
const formData = new FormData();
formData.append("mobNumId", mobId);
formData.append("otp", enteredOtp);
const res = await fetch(`${contextPath}api/verify-otp`, {
method: "POST",
body: formData, // sends as multipart/form-data
});
const data = await res.json();
if (res.ok && data.resStat) {
// OTP verified successfully
messageEl.textContent = data.res || "OTP verified successfully!";
messageEl.style.color = "green";
otpSection.style.display = "none";
sendOtpBtn.disabled = true;
await reloadMobileDropdown();
// Close modal, then redirect to view-login-det
setTimeout(() => {
closeLoginModal();
window.open(`${contextPath}view-login-det`, '_blank');
}, 500);
} else {
// OTP verification failed or session expired
messageEl.textContent = data.res || "OTP verification failed. Please request a new one.";
messageEl.style.color = "red";
sendOtpBtn.textContent = "Get OTP";
otpSection.style.display = "none"; // hide OTP field
otpSent = false; // reset flag → next click sends a new OTP
otpInput.value = ""; // clear OTP field only
await reloadMobileDropdown();
// If session expired, allow new OTP request
if (data.res && data.res.includes("Session expired")) {
mobNumInput.disabled = false; // ensure user can still use the same number
}
}
}
} catch (err) {
console.error("Error:", err);
messageEl.textContent = "Something went wrong. Please try again.";
messageEl.style.color = "red";
sendOtpBtn.textContent = otpSent ? "Verify OTP" : "Get OTP";
} finally {
sendOtpBtn.disabled = false;
}
});
});
/* === PAGE BUTTON FILTER & SEARCH === */
document.addEventListener("DOMContentLoaded", function() {
const buttonContainer = document.querySelector(".button-row");
if (!buttonContainer) return;
const buttons = Array.from(buttonContainer.querySelectorAll(".custom-button"));
function getButtonName(btn) {
return btn.childNodes[btn.childNodes.length - 1].textContent.trim().toLowerCase();
}
const searchInput = document.getElementById("search-input");
const alphabetSelect = document.getElementById("search-input2");
const clearBtn = document.getElementById("clear-btn");
const noResultsMsg = document.getElementById("no-res-msg");
/* === Reusable Dropdown Renderer === */
function populateAlphabetDropdown(onlyVisible = false) {
const prevSelected = alphabetSelect.value; // keep previous selection
// Reset dropdown
alphabetSelect.innerHTML = '<option value="">-</option>';
const letters = new Set();
buttons.forEach(btn => {
if (onlyVisible && btn.style.display === "none") return;
const name = getButtonName(btn);
if (name.length > 0) {
letters.add(name[0].toUpperCase());
}
});
const sortedLetters = [...letters].sort();
sortedLetters.forEach(letter => {
const opt = document.createElement("option");
opt.value = letter;
opt.textContent = letter;
alphabetSelect.appendChild(opt);
});
// Restore previous selection if still valid
if (letters.has(prevSelected.toUpperCase())) {
alphabetSelect.value = prevSelected.toUpperCase();
} else {
alphabetSelect.value = "";
}
}
// Initial dropdown load (all buttons)
populateAlphabetDropdown(false);
/* === FILTER BUTTONS === */
function updateButtonVisibility() {
const query = searchInput.value.trim().toLowerCase();
const selectedLetter = alphabetSelect.value.toLowerCase();
let matchedButtons = 0;
buttons.forEach(btn => {
const name = getButtonName(btn);
const matchesLetter =
selectedLetter === "" || name.startsWith(selectedLetter);
const matchesSearch =
query === "" || name.includes(query);
if (matchesLetter && matchesSearch) {
btn.style.display = "inline-block";
matchedButtons++;
} else {
btn.style.display = "none";
}
});
noResultsMsg.style.display = matchedButtons > 0 ? "none" : "block";
// Show or hide clear button
clearBtn.style.display =
(searchInput.value.trim() !== "" || alphabetSelect.value !== "")
? "inline-block"
: "none";
// Rebuild dropdown based on visible buttons
populateAlphabetDropdown(true);
}
/* === CLEAR BUTTON HANDLER === */
clearBtn.addEventListener("click", function() {
searchInput.value = "";
alphabetSelect.value = "";
clearBtn.style.display = "none";
// Reset all buttons visible
buttons.forEach(btn => btn.style.display = "inline-block");
// Rebuild dropdown from all buttons
populateAlphabetDropdown(false);
noResultsMsg.style.display = "none";
});
/* === EVENT LISTENERS === */
alphabetSelect.addEventListener("change", updateButtonVisibility);
searchInput.addEventListener("keyup", updateButtonVisibility);
// Initial render
updateButtonVisibility();
});

28
src/main/resources/static/assets/js/login-script.js

@ -0,0 +1,28 @@
function handleLoginSubmit2(event) {
event.preventDefault(); // prevent normal form submission
const form = document.getElementById("frm-usr-login-det");
const formData = new FormData(form);
fetch(contextPath + 'api/val-app-usr-login', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.result) {
let usrName = data.usrName && data.usrName.length > 0 ? data.usrName : null;
if (usrName) {
alert("Login Success");
window.location.href = contextPath + 'redirect';
} else {
alert("No role found for this user!");
}
} else {
alert(data.error || "Login failed");
}
})
.catch(err => {
console.error("Error during login:", err);
});
}

136
src/main/resources/static/assets/js/update-url-data-script.js

@ -0,0 +1,136 @@
// Global flags
let otpSent = false;
let isAdmin = false;
async function loadRole() {
try {
const res = await fetch(`${contextPath}api/get-role`, {
method: "POST"
});
const data = await res.json();
if (!data.authenticated) {
isAdmin = false;
return;
}
isAdmin = data.isAdmin;
} catch (e) {
// swallow network errors
console.error("Error: ", error);
}
}
// Open the login modal
function openLoginModal() {
if (isAdmin) {
// For admin → directly go to the post-verification page
window.open(`${contextPath}view-login-det`, '_blank');
return;
}
const modal = document.getElementById("login-modal");
modal.classList.remove("hide");
modal.classList.add("show");
modal.style.display = "flex"; // make it visible immediately before animation
}
// Load role when page loads
document.addEventListener("DOMContentLoaded", () => {
loadRole();
});
document.addEventListener("DOMContentLoaded", function() {
const dropdown = document.getElementById("urlDropdown");
const urlStatDropdown = document.getElementById("urlStatDropdown");
const urlLinkInput = document.getElementById("urlDataLink");
const urlNameInput = document.getElementById("urlDataName");
const urlUsrNameInput = document.getElementById("urlDataUsrName");
const urlUsrPassInput = document.getElementById("urlDataUsrPass");
const urlCatNameInput = document.getElementById("urlCatName");
const togglePass = document.getElementById("togglePass");
// safety check
if (!dropdown || !togglePass) {
return;
}
// if (!dropdown) return;
// --- Dropdown change handler ---
dropdown.addEventListener("change", function() {
const selected = dropdown.options[dropdown.selectedIndex];
if (!selected.value) {
urlLinkInput.value = "";
urlNameInput.value = "";
urlUsrNameInput.value = "";
urlUsrPassInput.value = "";
urlCatNameInput.value = "";
return;
}
const statId = selected.getAttribute("data-statId");
if (statId) {
urlStatDropdown.value = statId;
}
const link = selected.getAttribute("data-link");
const name = selected.getAttribute("data-name");
const usrname = selected.getAttribute("data-usrname");
const pass = selected.getAttribute("data-pass");
const urlCatName = selected.getAttribute("data-urlCatName");
urlLinkInput.value = link || "";
urlNameInput.value = name || "";
urlUsrNameInput.value = (usrname && usrname !== "-") ? usrname : "";
urlUsrPassInput.value = (pass && pass !== "-") ? pass : "";
urlCatNameInput.value = (urlCatName && urlCatName !== "-") ? urlCatName : "";
// Reset to hidden state
urlUsrPassInput.type = "password";
togglePass.classList.add("fa-eye");
togglePass.classList.remove("fa-eye-slash");
});
// --- Toggle Password Visibility ---
togglePass.addEventListener("click", function() {
const isPassword = urlUsrPassInput.type === "password";
urlUsrPassInput.type = isPassword ? "text" : "password";
togglePass.classList.toggle("fa-eye");
togglePass.classList.toggle("fa-eye-slash");
});
const form = document.getElementById("frm-url-det");
// Existing form submission logic...
form.addEventListener("submit", function(e) {
e.preventDefault(); // Prevent default form submission
const formData = new FormData(form);
fetch(form.getAttribute("action"), {
method: "POST",
body: formData
})
.then(response => {
if (!response.ok) {
throw new Error("Network response was not ok: " + response.statusText);
}
return response.json();
})
.then(data => {
if (data.resStat) {
alert("Success: " + data.res);
form.reset();
} else {
alert("Failed: " + data.res);
}
})
.catch(error => {
console.error("Error updating url:", error);
alert("Error updating url. Please try again.");
});
});
});

56
src/main/resources/static/assets/js/view-login-details-script.js

@ -0,0 +1,56 @@
document.addEventListener("DOMContentLoaded", function() {
// Auto-close window after 2 minutes (120000 ms)
setTimeout(() => {
window.close();
}, 120000);
// Initialize DataTables
const table = $("#url-det-tbl").DataTable({
pageLength: -1, // default = show ALL rows
lengthMenu: [
[5, 10, 20, 50, -1], // values
[5, 10, 20, 50, "All"] // labels
],
ordering: false, // Disable client-side sorting
language: {
info: "Showing _START_ to _END_ of _TOTAL_ entries",
lengthMenu: "Show _MENU_ entries"
},
dom: 'lfrtip', // hides default search bar
// Auto-insert S.No
drawCallback: function(settings) {
let api = this.api();
api.rows({ page: 'current' }).every(function(rowIdx, tableLoop, rowLoop) {
let index = rowIdx + 1;
api.cell(rowIdx, 0).data(index);
});
}
});
// Move DataTables elements to your custom divs
$('#custom-search').html($('.dataTables_filter'));
$('#custom-pagination').html($('.dataTables_paginate'));
$('#custom-info').html($('.dataTables_info'));
$('#custom-length').html($('.dataTables_length'));
});
const toggleIcons = document.querySelectorAll(".toggle-pass");
toggleIcons.forEach(icon => {
icon.addEventListener("click", function() {
const targetId = this.getAttribute("data-target");
const passwordElement = document.getElementById(targetId);
const originalPassword = this.getAttribute("data-pass");
if (passwordElement.textContent.includes("*")) {
// Show actual password
passwordElement.textContent = originalPassword;
this.classList.replace("fa-eye", "fa-eye-slash");
} else {
// Hide password
passwordElement.textContent = "*".repeat(originalPassword.length);
this.classList.replace("fa-eye-slash", "fa-eye");
}
});
});

1123
src/main/resources/static/assets/popper-2.11.8/js/popper.min.js
File diff suppressed because it is too large
View File

BIN
src/main/resources/static/assets/poppins-23.0/fonts/devanagari/poppins-v23-devanagari-100.woff2

BIN
src/main/resources/static/assets/poppins-23.0/fonts/devanagari/poppins-v23-devanagari-200.woff2

BIN
src/main/resources/static/assets/poppins-23.0/fonts/devanagari/poppins-v23-devanagari-300.woff2

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save