├── .gitignore ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── Dockerfile ├── LICENSE.md ├── mvnw ├── mvnw.cmd ├── pom.xml ├── readme.md └── src ├── main ├── java │ └── com │ │ └── systango │ │ └── springboard │ │ ├── ServletInitializer.java │ │ ├── SpringboardApplication.java │ │ ├── api │ │ ├── .gitkeep │ │ └── v1 │ │ │ ├── controller │ │ │ ├── AboutController.java │ │ │ ├── AdminController.java │ │ │ ├── PaymentController.java │ │ │ ├── UIController.java │ │ │ └── UserController.java │ │ │ └── request │ │ │ ├── AddFaqRequest.java │ │ │ ├── AddUserRequest.java │ │ │ └── WalletRequest.java │ │ ├── configuration │ │ └── SpringBoardConfiguration.java │ │ ├── domain │ │ ├── model │ │ │ ├── BaseDomainObject.java │ │ │ ├── Faq.java │ │ │ ├── Settings.java │ │ │ ├── payment │ │ │ │ ├── WalletDetails.java │ │ │ │ └── WalletLevel.java │ │ │ └── user │ │ │ │ ├── ApplicationUser.java │ │ │ │ └── Role.java │ │ └── repository │ │ │ ├── ApplicationUserRepository.java │ │ │ ├── FaqRepository.java │ │ │ ├── SettingsRepository.java │ │ │ └── WalletRepository.java │ │ ├── dto │ │ ├── model │ │ │ ├── FaqDto.java │ │ │ ├── payment │ │ │ │ └── WalletDto.java │ │ │ └── user │ │ │ │ └── UserDto.java │ │ └── response │ │ │ ├── Response.java │ │ │ └── ResponseError.java │ │ ├── mapper │ │ ├── FaqMapper.java │ │ └── WalletMapper.java │ │ ├── security │ │ ├── JWTAuthenticationFilter.java │ │ ├── JWTAuthorizationFilter.java │ │ ├── SecurityConstants.java │ │ ├── UserDetailsServiceImpl.java │ │ └── WebSecurity.java │ │ ├── service │ │ ├── admin │ │ │ ├── AdminService.java │ │ │ └── AdminServiceImpl.java │ │ ├── exception │ │ │ ├── AdminException.java │ │ │ ├── PaymentException.java │ │ │ └── UserException.java │ │ ├── payment │ │ │ ├── PaymentService.java │ │ │ └── PaymentServiceImpl.java │ │ └── user │ │ │ ├── UserService.java │ │ │ └── UserServiceImpl.java │ │ └── util │ │ ├── AssertUtils.java │ │ └── SwaggerConfig.java └── resources │ ├── application.properties │ ├── banner.txt │ ├── db │ └── migration │ │ ├── V1.1__Init_DB.sql │ │ └── V1.2__Init_DB.sql │ ├── logback.xml │ ├── static │ ├── css │ │ ├── _custom.scss │ │ ├── _variables.scss │ │ ├── bootstrap.css │ │ ├── bootstrap.min.css │ │ └── main.css │ ├── favicon.ico │ ├── img │ │ ├── apple_store.png │ │ ├── blog-1.jpg │ │ ├── blog-2.jpg │ │ ├── blog-3.jpg │ │ ├── favicons.png │ │ ├── google-play.png │ │ ├── product.png │ │ ├── product2.jpg │ │ ├── shot-3.png │ │ ├── springboard-logo.png │ │ ├── team-1.jpg │ │ ├── team-2.jpg │ │ ├── team-3.jpg │ │ └── team-4.jpg │ ├── index.html │ └── js │ │ └── scripts.js │ └── templates │ ├── error │ └── 403.html │ ├── fragments │ ├── footer.html │ └── header.html │ └── login.html └── test └── java └── com └── systango └── springboard └── SpringboardApplicationTests.java /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | 12 | ### IntelliJ IDEA ### 13 | .idea 14 | *.iws 15 | *.iml 16 | *.ipr 17 | 18 | ### NetBeans ### 19 | nbproject/private/ 20 | build/ 21 | nbbuild/ 22 | dist/ 23 | nbdist/ 24 | .nb-gradle/ 25 | 26 | # User-specific stuff: 27 | .idea/**/workspace.xml 28 | .idea/**/tasks.xml 29 | .idea/dictionaries 30 | 31 | # Sensitive or high-churn files: 32 | .idea/**/dataSources/ 33 | .idea/**/dataSources.ids 34 | .idea/**/dataSources.xml 35 | .idea/**/dataSources.local.xml 36 | .idea/**/sqlDataSources.xml 37 | .idea/**/dynamic.xml 38 | .idea/**/uiDesigner.xml 39 | 40 | # Gradle: 41 | .idea/**/gradle.xml 42 | .idea/**/libraries 43 | 44 | # CMake 45 | cmake-build-debug/ 46 | cmake-build-release/ 47 | 48 | # Mongo Explorer plugin: 49 | .idea/**/mongoSettings.xml 50 | 51 | ## File-based project format: 52 | *.iws 53 | 54 | ## Plugin-specific files: 55 | 56 | # IntelliJ 57 | out/ 58 | 59 | # mpeltonen/sbt-idea plugin 60 | .idea_modules/ 61 | 62 | # JIRA plugin 63 | atlassian-ide-plugin.xml 64 | 65 | # Cursive Clojure plugin 66 | .idea/replstate.xml 67 | 68 | # Crashlytics plugin (for Android Studio and IntelliJ) 69 | com_crashlytics_export_strings.xml 70 | crashlytics.properties 71 | crashlytics-build.properties 72 | fabric.properties 73 | 74 | # Mac OS 75 | .DS_Store 76 | 77 | # Logs 78 | logs/ 79 | .log -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SystangoTechnologies/Springboard/e31e3d3eea44eeda07bd8c1bfa82224f2d016139/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.2/apache-maven-3.5.2-bin.zip 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM tomcat:8.0.20-jre8 2 | MAINTAINER Arpit Khandelwal 3 | VOLUME /tmp 4 | EXPOSE 8090 5 | COPY target/springboard-0.0.1-SNAPSHOT.war /usr/local/tomcat/webapps/app.war 6 | RUN sh -c 'touch /usr/local/tomcat/webapps/app.war' 7 | ENTRYPOINT [ "sh", "-c", "java -Djava.security.egd=file:/dev/./urandom -jar /usr/local/tomcat/webapps/app.war"] 8 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2018] [Systango Technologies] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Migwn, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 204 | echo $MAVEN_PROJECTBASEDIR 205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 206 | 207 | # For Cygwin, switch paths to Windows format before running java 208 | if $cygwin; then 209 | [ -n "$M2_HOME" ] && 210 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 211 | [ -n "$JAVA_HOME" ] && 212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 213 | [ -n "$CLASSPATH" ] && 214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 215 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 217 | fi 218 | 219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 220 | 221 | exec "$JAVACMD" \ 222 | $MAVEN_OPTS \ 223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 226 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.systango 7 | springboard 8 | 1.0.1-SNAPSHOT 9 | war 10 | 11 | springboard 12 | API boilerplate project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.1.7.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 0.9.1 26 | 2.9.2 27 | 2.9.2 28 | 2.6 29 | 30 | 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-actuator 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-aop 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-data-jpa 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter-security 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-starter-thymeleaf 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-starter-web 55 | 56 | 57 | org.springframework.data 58 | spring-data-envers 59 | 60 | 61 | org.springframework.boot 62 | spring-boot-devtools 63 | true 64 | 65 | 66 | org.postgresql 67 | postgresql 68 | 42.2.6 69 | runtime 70 | 71 | 72 | org.projectlombok 73 | lombok 74 | true 75 | 76 | 77 | org.springframework.boot 78 | spring-boot-starter-tomcat 79 | 80 | 81 | 82 | org.springframework.boot 83 | spring-boot-starter-test 84 | test 85 | 86 | 87 | org.springframework.restdocs 88 | spring-restdocs-mockmvc 89 | test 90 | 91 | 92 | org.springframework.security 93 | spring-security-test 94 | test 95 | 96 | 97 | io.jsonwebtoken 98 | jjwt 99 | ${jsonwebtoken.version} 100 | 101 | 102 | io.springfox 103 | springfox-swagger2 104 | ${springfox.swagger.version} 105 | 106 | 107 | io.springfox 108 | springfox-swagger-ui 109 | ${springfox.swagger.ui.version} 110 | 111 | 112 | commons-io 113 | commons-io 114 | ${commons.io.version} 115 | 116 | 117 | org.flywaydb 118 | flyway-core 119 | 120 | 121 | 122 | 123 | 124 | 125 | org.springframework.boot 126 | spring-boot-maven-plugin 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ![Springboard](https://github.com/SystangoTechnologies/Springboard/blob/master/src/main/resources/static/img/springboard-logo.png) 2 | 3 | ## Springboard 4 | Production ready maven based Spring Boot starter kit application. 5 | 6 | ## Description 7 | Starter kit for jump starting the development of a API oriented spring based Java server. It contains the best practices and latest tools that a spring boot developer should opt for in a fresh development. Since JPA is used, developers are free to opt for any SQL based DB engine for persistence (Postgres has been used as an example with this code sample). The preferred IDE for development is IntelliJ which comes with a plethora of useful JAVA tools to support Spring Boot development, but devlopers are free to opt for Eclipse or STS as well. 8 | 9 | ## Technology 10 | 11 | - **Spring Boot** - Server side framework 12 | - **JPA** - Entity framework 13 | - **Lombok** - Provides automated getter/setters 14 | - **Actuator** - Application insights on the fly 15 | - **Spring Security** - Spring's security layer 16 | - **Thymeleaf** - Template Engine 17 | - **Devtools** - Support Hot-Code Swapping with live browser reload 18 | - **JJWT** - JWT tokens for API authentication 19 | - **Swagger** - In-built swagger documentation support 20 | - **Docker** - Docker containers 21 | - **Junit** - Unit testing framework 22 | 23 | 24 | ## Application Structure 25 | ```` 26 | |____src 27 | | |____main 28 | | | |____java 29 | | | | |____com 30 | | | | | |____systango 31 | | | | | | |____springboard 32 | | | | | | | |____api 33 | | | | | | | | |____v1 34 | | | | | | | | | |____controller 35 | | | | | | | | | |____request 36 | | | | | | | |____domain 37 | | | | | | | | |____model 38 | | | | | | | | | |____user 39 | | | | | | | | |____repository 40 | | | | | | | |____dto 41 | | | | | | | | |____model 42 | | | | | | | | | |____user 43 | | | | | | | | |____response 44 | | | | | | | |____mapper 45 | | | | | | | |____security 46 | | | | | | | |____service 47 | | | | | | | | |____admin 48 | | | | | | | | |____exception 49 | | | | | | | | |____user 50 | | | | | | | |____util 51 | | | |____resources 52 | | | | |____static 53 | | | | | |____css 54 | | | | | |____img 55 | | | | | |____js 56 | | | | |____templates 57 | | | | |____db 58 | | | | | |____migration 59 | | |____test 60 | | | |____java 61 | | | | |____com 62 | | | | | |____systango 63 | | | | | | |____springboard 64 | 65 | ```` 66 | 67 | ## Database Migrations 68 | **Flyway** library is integrated to manage database migrations. The migration files have to be kept in ``src/main/resources/db/migration`` folder. There are sample migrations present here to depict file name patterns. Flyway will create a table in the database to manage the schema version files. 69 | 70 | ## Running the server locally 71 | The pom.xml can be configured to generate a WAR or standalone executable jar file as well. We suggest developers should use JAR in the development environment as the hot swapping and live reload features in Spring Boot are compatible with it and for production deployment WAR packaging should be used with standalone Tomcat (or any other servlet container) server. 72 | 73 | ## Docker 74 | Springboard supports docker container out of the box. This framework is meant to cater to both web based applications as well as scalable micro services written in Java. The 'Dockerfile' supplied with this boilerplate does a basic job in terms of instructing which tomcat image to pick and how to deploy the war file to the container. Please follow the steps mentioned below in order to create and deploy the docker container from this installation - 75 | 76 | * Create docker image from this project - 77 | ```` 78 | docker build -t springboard . 79 | 80 | ```` 81 | 82 | * Once image is created, you may run the same using the following command - 83 | ```` 84 | docker run -p 8090:8090 springboard 85 | 86 | ```` 87 | 88 | * Since we have not included a postgressql inside this container and are trying to connect to a database that is external to the container, you may need to enable the databsae to allow connections from foreign IP addresses which is disabled in case of default installations. 89 | 90 | * Some handy dcoker comammnds are listed below, we found them useful during the course of this development and thought its worth sharing. 91 | - In order to delete all images, use the following command 92 | ```` 93 | docker rmi --force $(docker images -q) 94 | ```` 95 | - In order to delete all containers, use the following command 96 | ```` 97 | docker rm $(docker ps -a -q) 98 | ```` 99 | - In order to list all the running docker containers, use the following command 100 | ```` 101 | docker ps 102 | ```` 103 | - In order to list all the docker images, use the following command 104 | ```` 105 | docker images 106 | ```` 107 | - In order to get inside a docker container's command prompt, use the following command 108 | ```` 109 | docker exec -it bash 110 | ```` 111 | 112 | ## Unit test cases 113 | Springboard comes with a handy set of unit test cases present in the 'SpringboardApplicationTests' class inside the test directory. The test cases are based on the fact that spring security is applied, the first thing you should do before accessing a protected url in the application is obtain a valid JWT token using the utility method provided in the test suite. Once you have the valid token, any protected url could be unit tested without any issues. 114 | 115 | ## Swagger Documentation 116 | Swagger documentation is in-built in this starter-kit and can be accessed at the following URL - 117 | ```` 118 | http://:8090/swagger-ui.html 119 | ```` 120 | The configuration for swagger are driven via com.systango.springboard.util.SwaggerConfig.java class that at present shows all the apis listed under the "com.systango.springboard.api.*"package. 121 | The JWT token should be presented inside the value input box as "Bearer token_goes_here". The authorization header for the REST APIs should be as follows - 122 | ```` 123 | curl -X GET \ 124 | http://localhost:8090/v1/about/faq \ 125 | -H 'Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTUxNTMyMjU4Mn0.RHex52JtZZt0lfaZc_sHdwU8auevcT9COJDxp6RbBBU1ZbCEc6bh4b2CxjA8TSgH7DkwWIbO6ISqpMuUXNcZwA' \ 126 | -H 'Cache-Control: no-cache' \ 127 | ```` 128 | 129 | ## Contributors 130 | [Arpit Khandelwal](https://www.linkedin.com/in/arpitkhandelwal1984/) 131 | [Sumit Singhal](https://www.linkedin.com/in/s-singhal/) 132 | 133 | ## License 134 | This project is licensed under the terms of the MIT license. -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/ServletInitializer.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard; 2 | 3 | import org.springframework.boot.builder.SpringApplicationBuilder; 4 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; 5 | 6 | public class ServletInitializer extends SpringBootServletInitializer { 7 | 8 | @Override 9 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { 10 | return application.sources(SpringboardApplication.class); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/SpringboardApplication.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard; 2 | 3 | import org.flywaydb.core.Flyway; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.boot.autoconfigure.flyway.FlywayMigrationInitializer; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.data.web.config.EnableSpringDataWebSupport; 10 | 11 | @SpringBootApplication() 12 | @EnableSpringDataWebSupport 13 | public class SpringboardApplication { 14 | 15 | @Value("${flyway.baseline.on.migrate}") 16 | private boolean flywayBaselineOnMigrate; 17 | 18 | public static void main(String[] args) { 19 | SpringApplication.run(SpringboardApplication.class, args); 20 | } 21 | 22 | @Bean 23 | public FlywayMigrationInitializer flywayInitializer(Flyway flyway, @Value("${flyway.baseline.on.migrate: true}") boolean baseline) { 24 | flyway.baseline(); 25 | return new FlywayMigrationInitializer(flyway); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/api/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SystangoTechnologies/Springboard/e31e3d3eea44eeda07bd8c1bfa82224f2d016139/src/main/java/com/systango/springboard/api/.gitkeep -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/api/v1/controller/AboutController.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.api.v1.controller; 2 | 3 | 4 | import com.systango.springboard.domain.repository.FaqRepository; 5 | import com.systango.springboard.domain.repository.SettingsRepository; 6 | import com.systango.springboard.dto.response.Response; 7 | import com.systango.springboard.mapper.FaqMapper; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.http.MediaType; 10 | import org.springframework.web.bind.annotation.GetMapping; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.RestController; 13 | 14 | /** 15 | * Created by Arpit Khandelwal. 16 | */ 17 | @RestController 18 | @RequestMapping("/v1/about") 19 | public class AboutController { 20 | 21 | private SettingsRepository settingsRepository; 22 | private FaqRepository faqRepository; 23 | 24 | 25 | @Autowired 26 | public AboutController(FaqRepository faqRepository, SettingsRepository settingsRepository) { 27 | this.faqRepository = faqRepository; 28 | this.settingsRepository = settingsRepository; 29 | } 30 | 31 | @GetMapping(value = "/faq", produces = MediaType.APPLICATION_JSON_VALUE) 32 | public Response getFaq() { 33 | return Response.ok().setPayload(FaqMapper.mapFaqs(faqRepository.findAll())); 34 | } 35 | 36 | @GetMapping(value = "/terms-and-conditions", produces = MediaType.APPLICATION_JSON_VALUE) 37 | public Response getTermsAndConditions() { 38 | return Response.ok().setPayload(settingsRepository.findTermsAndConditions().getSettingValue()); 39 | } 40 | 41 | @GetMapping(value = "/privacy-policy", produces = MediaType.APPLICATION_JSON_VALUE) 42 | public Response getPrivacyPolicy() { 43 | return Response.ok().setPayload(settingsRepository.findPrivacyPolicy().getSettingValue()); 44 | } 45 | 46 | @GetMapping(value = "/about-us", produces = MediaType.APPLICATION_JSON_VALUE) 47 | public Response getAboutUs() { 48 | return Response.ok().setPayload(settingsRepository.findAbout().getSettingValue()); 49 | } 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/api/v1/controller/AdminController.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.api.v1.controller; 2 | 3 | 4 | import com.systango.springboard.api.v1.request.AddFaqRequest; 5 | import com.systango.springboard.dto.model.FaqDto; 6 | import com.systango.springboard.dto.response.Response; 7 | import com.systango.springboard.service.admin.AdminService; 8 | import com.systango.springboard.service.exception.AdminException; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.http.MediaType; 11 | import org.springframework.security.access.prepost.PreAuthorize; 12 | import org.springframework.web.bind.annotation.PostMapping; 13 | import org.springframework.web.bind.annotation.RequestBody; 14 | import org.springframework.web.bind.annotation.RequestMapping; 15 | import org.springframework.web.bind.annotation.RestController; 16 | 17 | import javax.validation.Valid; 18 | 19 | /** 20 | * Created by Arpit Khandelwal. 21 | */ 22 | @RestController 23 | @RequestMapping("/v1/admin") 24 | public class AdminController { 25 | 26 | private final AdminService adminService; 27 | 28 | @Autowired 29 | public AdminController(AdminService adminService) { 30 | this.adminService = adminService; 31 | } 32 | 33 | @PostMapping(path = "/add-faq", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) 34 | @PreAuthorize("hasAuthority('ROLE_ADMIN')") 35 | public Response addFaq(@RequestBody @Valid AddFaqRequest addFaqRequest) throws AdminException { 36 | FaqDto faqDto = new FaqDto() 37 | .setQuestion(addFaqRequest.getQuestion()) 38 | .setAnswer(addFaqRequest.getAnswer()); 39 | 40 | return Response.ok().setPayload(adminService.addFaq(faqDto)); 41 | } 42 | 43 | } 44 | 45 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/api/v1/controller/PaymentController.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.api.v1.controller; 2 | 3 | import com.systango.springboard.api.v1.request.WalletRequest; 4 | import com.systango.springboard.dto.model.payment.WalletDto; 5 | import com.systango.springboard.dto.response.Response; 6 | import com.systango.springboard.mapper.WalletMapper; 7 | import com.systango.springboard.service.exception.PaymentException; 8 | import com.systango.springboard.service.payment.PaymentService; 9 | 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.http.MediaType; 12 | import org.springframework.http.ResponseEntity; 13 | import org.springframework.web.bind.annotation.*; 14 | 15 | import javax.validation.Valid; 16 | 17 | /** 18 | * Created by Arpit Khandelwal. 19 | */ 20 | @RestController 21 | @RequestMapping("/v1/payment") 22 | public class PaymentController { 23 | 24 | PaymentService paymentService; 25 | 26 | public PaymentController(PaymentService paymentService) { 27 | this.paymentService = paymentService; 28 | } 29 | 30 | @PostMapping("/registerwallet") 31 | public ResponseEntity registerWallet(@RequestBody @Valid WalletRequest walletRequest) throws PaymentException { 32 | WalletDto walletDto = new WalletDto() 33 | .setUserName(walletRequest.getUsername()) 34 | .setWalletLevel(walletRequest.getWalletLevel()); 35 | paymentService.createUsersWallet(walletDto); 36 | return new ResponseEntity(HttpStatus.CREATED); 37 | } 38 | 39 | @GetMapping(value = "/wallet/{username}", produces = MediaType.APPLICATION_JSON_VALUE) 40 | public Response getWalletDetails(@PathVariable String username) throws PaymentException { 41 | return Response.ok().setPayload(WalletMapper.mapWallet(paymentService.getWalletDetails(username))); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/api/v1/controller/UIController.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.api.v1.controller; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | 7 | /** 8 | * Created by Arpit Khandelwal. 9 | */ 10 | @Controller 11 | @RequestMapping("/") 12 | public class UIController { 13 | 14 | @GetMapping("/login") 15 | public String login() { 16 | return "login"; 17 | } 18 | 19 | @GetMapping("/403") 20 | public String error403() { 21 | return "/error/403"; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/api/v1/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.api.v1.controller; 2 | 3 | import com.systango.springboard.api.v1.request.AddUserRequest; 4 | import com.systango.springboard.dto.model.user.UserDto; 5 | import com.systango.springboard.service.exception.UserException; 6 | import com.systango.springboard.service.user.UserService; 7 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 8 | import org.springframework.web.bind.annotation.PostMapping; 9 | import org.springframework.web.bind.annotation.RequestBody; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | import org.springframework.web.bind.annotation.RestController; 12 | 13 | import javax.validation.Valid; 14 | 15 | /** 16 | * Created by Arpit Khandelwal. 17 | */ 18 | @RestController 19 | @RequestMapping("/v1/user") 20 | public class UserController { 21 | private UserService userService; 22 | private BCryptPasswordEncoder bCryptPasswordEncoder; 23 | 24 | public UserController(UserService userService, 25 | BCryptPasswordEncoder bCryptPasswordEncoder) { 26 | this.userService = userService; 27 | this.bCryptPasswordEncoder = bCryptPasswordEncoder; 28 | } 29 | 30 | @PostMapping("/signup") 31 | public void signUp(@RequestBody @Valid AddUserRequest addUserRequest) throws UserException { 32 | UserDto userDto = new UserDto() 33 | .setUsername(addUserRequest.getUsername()) 34 | .setPassword(bCryptPasswordEncoder.encode(addUserRequest.getPassword())); 35 | userService.addUser(userDto); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/api/v1/request/AddFaqRequest.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.api.v1.request; 2 | 3 | import javax.validation.constraints.NotEmpty; 4 | 5 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 6 | import lombok.Getter; 7 | import lombok.NoArgsConstructor; 8 | import lombok.Setter; 9 | import lombok.experimental.Accessors; 10 | 11 | 12 | /** 13 | * Created by Arpit Khandelwal. 14 | */ 15 | @Getter 16 | @Setter 17 | @Accessors(chain = true) 18 | @NoArgsConstructor 19 | @JsonIgnoreProperties(ignoreUnknown = true) 20 | public class AddFaqRequest { 21 | @NotEmpty(message = "{constraints.NotEmpty.message}") 22 | private String question; 23 | @NotEmpty(message = "{constraints.NotEmpty.message}") 24 | private String answer; 25 | } -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/api/v1/request/AddUserRequest.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.api.v1.request; 2 | 3 | import javax.validation.constraints.NotEmpty; 4 | 5 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 6 | import lombok.Getter; 7 | import lombok.NoArgsConstructor; 8 | import lombok.Setter; 9 | import lombok.experimental.Accessors; 10 | 11 | /** 12 | * Created by Arpit Khandelwal. 13 | */ 14 | @Getter 15 | @Setter 16 | @Accessors(chain = true) 17 | @NoArgsConstructor 18 | @JsonIgnoreProperties(ignoreUnknown = true) 19 | public class AddUserRequest { 20 | @NotEmpty(message = "{constraints.NotEmpty.message}") 21 | private String username; 22 | @NotEmpty(message = "{constraints.NotEmpty.message}") 23 | private String password; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/api/v1/request/WalletRequest.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.api.v1.request; 2 | 3 | import javax.validation.constraints.NotEmpty; 4 | 5 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 6 | import com.systango.springboard.domain.model.payment.WalletLevel; 7 | import lombok.Getter; 8 | import lombok.NoArgsConstructor; 9 | import lombok.Setter; 10 | import lombok.experimental.Accessors; 11 | 12 | 13 | /** 14 | * Created by Arpit Khandelwal. 15 | */ 16 | @Getter 17 | @Setter 18 | @Accessors(chain = true) 19 | @NoArgsConstructor 20 | @JsonIgnoreProperties(ignoreUnknown = true) 21 | public class WalletRequest { 22 | @NotEmpty(message = "{constraints.NotEmpty.message}") 23 | private String username; 24 | 25 | private WalletLevel walletLevel; 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/configuration/SpringBoardConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.configuration; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 6 | 7 | /** 8 | * Created by Arpit Khandelwal. 9 | */ 10 | @Configuration 11 | public class SpringBoardConfiguration { 12 | 13 | @Bean 14 | public BCryptPasswordEncoder bCryptPasswordEncoder() { 15 | return new BCryptPasswordEncoder(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/domain/model/BaseDomainObject.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.domain.model; 2 | 3 | import lombok.Getter; 4 | import lombok.NoArgsConstructor; 5 | import lombok.Setter; 6 | import org.hibernate.annotations.CreationTimestamp; 7 | import org.hibernate.annotations.UpdateTimestamp; 8 | 9 | import javax.persistence.GeneratedValue; 10 | import javax.persistence.GenerationType; 11 | import javax.persistence.Id; 12 | import javax.persistence.MappedSuperclass; 13 | import java.io.Serializable; 14 | import java.util.Date; 15 | 16 | /** 17 | * Represents all common properties of standard domain objects 18 | * 19 | * @author Arpit Khandelwal 20 | */ 21 | @MappedSuperclass 22 | @Setter 23 | @Getter 24 | @NoArgsConstructor 25 | public class BaseDomainObject implements Serializable { 26 | 27 | @Id 28 | @GeneratedValue(strategy = GenerationType.IDENTITY) 29 | protected Long id; 30 | 31 | @CreationTimestamp 32 | protected Date createTimestamp = new Date(); 33 | 34 | @UpdateTimestamp 35 | protected Date lastEditTimestamp; 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/domain/model/Faq.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.domain.model; 2 | 3 | import lombok.Getter; 4 | import lombok.NoArgsConstructor; 5 | import lombok.Setter; 6 | import lombok.experimental.Accessors; 7 | 8 | import javax.persistence.Entity; 9 | 10 | /** 11 | * Created by Arpit Khandelwal. 12 | */ 13 | @Getter 14 | @Setter 15 | @NoArgsConstructor 16 | @Accessors(chain = true) 17 | @Entity 18 | public class Faq extends BaseDomainObject { 19 | 20 | private String question; 21 | private String answer; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/domain/model/Settings.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.domain.model; 2 | 3 | import lombok.Getter; 4 | import lombok.NoArgsConstructor; 5 | import lombok.Setter; 6 | import lombok.experimental.Accessors; 7 | 8 | import javax.persistence.Entity; 9 | 10 | /** 11 | * @author Arpit Khandelwal 12 | */ 13 | @Getter 14 | @Setter 15 | @NoArgsConstructor 16 | @Accessors(chain = true) 17 | @Entity 18 | public class Settings extends BaseDomainObject { 19 | 20 | private String settingKey; 21 | private String settingValue; 22 | 23 | } -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/domain/model/payment/WalletDetails.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.domain.model.payment; 2 | 3 | import com.systango.springboard.domain.model.BaseDomainObject; 4 | import com.systango.springboard.domain.model.user.ApplicationUser; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | import lombok.experimental.Accessors; 9 | 10 | import javax.persistence.*; 11 | import java.math.BigDecimal; 12 | 13 | /** 14 | * Created by Arpit Khandelwal. 15 | */ 16 | @Getter 17 | @Setter 18 | @NoArgsConstructor 19 | @Accessors(chain = true) 20 | @Entity 21 | public class WalletDetails extends BaseDomainObject { 22 | 23 | @Transient 24 | private BigDecimal balance; 25 | 26 | @Column 27 | @Enumerated(EnumType.STRING) 28 | private WalletLevel walletLevel; 29 | 30 | @OneToOne 31 | private ApplicationUser applicationUser; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/domain/model/payment/WalletLevel.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.domain.model.payment; 2 | 3 | import java.util.Optional; 4 | 5 | /** 6 | * @author Arpit Khandelwal 7 | */ 8 | public enum WalletLevel { 9 | 10 | WALLET_BITCOIN, WALLET_REGULAR; 11 | 12 | public static Optional getByName(String name) { 13 | for (WalletLevel walletLevel : values()) { 14 | if (walletLevel.name().equals(name)) { 15 | return Optional.of(walletLevel); 16 | } 17 | } 18 | return Optional.empty(); 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/domain/model/user/ApplicationUser.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.domain.model.user; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.systango.springboard.domain.model.BaseDomainObject; 5 | import com.systango.springboard.domain.model.payment.WalletDetails; 6 | import lombok.Getter; 7 | import lombok.NoArgsConstructor; 8 | import lombok.Setter; 9 | import lombok.experimental.Accessors; 10 | 11 | import javax.persistence.*; 12 | import java.util.List; 13 | 14 | /** 15 | * Created by Arpit Khandelwal. 16 | */ 17 | @Getter 18 | @Setter 19 | @NoArgsConstructor 20 | @Accessors(chain = true) 21 | @JsonIgnoreProperties (ignoreUnknown=true) 22 | @Entity 23 | public class ApplicationUser extends BaseDomainObject { 24 | 25 | private String username; 26 | private String password; 27 | private String firstName; 28 | private String lastName; 29 | private String email; 30 | private String phoneNumber; 31 | 32 | @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) 33 | @JoinTable(name = "user_roles", 34 | joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"), 35 | inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id")) 36 | private List roles; 37 | 38 | @OneToOne(cascade = CascadeType.ALL, mappedBy = "applicationUser") 39 | private WalletDetails walletDetails; 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/domain/model/user/Role.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.domain.model.user; 2 | 3 | import com.systango.springboard.domain.model.BaseDomainObject; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import lombok.experimental.Accessors; 8 | 9 | import javax.persistence.Entity; 10 | 11 | /** 12 | * Created by Arpit Khandelwal. 13 | */ 14 | @Getter 15 | @Setter 16 | @NoArgsConstructor 17 | @Accessors(chain = true) 18 | @Entity 19 | public class Role extends BaseDomainObject { 20 | 21 | private String name; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/domain/repository/ApplicationUserRepository.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.domain.repository; 2 | 3 | import com.systango.springboard.domain.model.user.ApplicationUser; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | /** 7 | * Created by Arpit Khandelwal. 8 | */ 9 | public interface ApplicationUserRepository extends JpaRepository { 10 | 11 | /** 12 | * Finds a user by userName field. 13 | * 14 | * @param username 15 | * @return 16 | */ 17 | ApplicationUser findByUsername(String username); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/domain/repository/FaqRepository.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.domain.repository; 2 | 3 | import com.systango.springboard.domain.model.Faq; 4 | import org.springframework.data.repository.CrudRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Created by Arpit Khandelwal. 11 | */ 12 | @Repository 13 | public interface FaqRepository extends CrudRepository { 14 | 15 | @Override 16 | List findAll(); 17 | 18 | /** 19 | * Find a FAQ using the question string. 20 | * 21 | * @param question 22 | * @return 23 | */ 24 | Faq findByQuestion(String question); 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/domain/repository/SettingsRepository.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.domain.repository; 2 | 3 | import com.systango.springboard.domain.model.Settings; 4 | import org.springframework.data.jpa.repository.Query; 5 | import org.springframework.data.repository.CrudRepository; 6 | import org.springframework.stereotype.Repository; 7 | 8 | /** 9 | * @author Arpit Khandelwal 10 | */ 11 | @Repository 12 | public interface SettingsRepository extends CrudRepository { 13 | 14 | @Query("SELECT s FROM Settings s WHERE s.settingKey = 'about'") 15 | Settings findAbout(); 16 | 17 | @Query("SELECT s FROM Settings s WHERE s.settingKey = 'terms_and_conditions'") 18 | Settings findTermsAndConditions(); 19 | 20 | @Query("SELECT s FROM Settings s WHERE s.settingKey = 'privacy_policy'") 21 | Settings findPrivacyPolicy(); 22 | 23 | } -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/domain/repository/WalletRepository.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.domain.repository; 2 | 3 | import com.systango.springboard.domain.model.payment.WalletDetails; 4 | import org.springframework.data.repository.CrudRepository; 5 | 6 | /** 7 | * Created by Arpit Khandelwal. 8 | */ 9 | public interface WalletRepository extends CrudRepository { 10 | /** 11 | * List the wallet details for a given userID 12 | * 13 | * @param userID 14 | * @return 15 | */ 16 | WalletDetails findByApplicationUser_Id(Long userID); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/dto/model/FaqDto.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.dto.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | import lombok.ToString; 9 | import lombok.experimental.Accessors; 10 | 11 | /** 12 | * Created by Arpit Khandelwal. 13 | */ 14 | @Getter 15 | @Setter 16 | @Accessors(chain = true) 17 | @NoArgsConstructor 18 | @ToString 19 | @JsonInclude(value = JsonInclude.Include.NON_NULL) 20 | @JsonIgnoreProperties(ignoreUnknown = true) 21 | public class FaqDto { 22 | 23 | private String question; 24 | private String answer; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/dto/model/payment/WalletDto.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.dto.model.payment; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.systango.springboard.domain.model.payment.WalletLevel; 6 | import lombok.Getter; 7 | import lombok.NoArgsConstructor; 8 | import lombok.Setter; 9 | import lombok.ToString; 10 | import lombok.experimental.Accessors; 11 | 12 | import java.math.BigDecimal; 13 | 14 | /** 15 | * @author Arpit Khandelwal 16 | */ 17 | @Getter 18 | @Setter 19 | @Accessors(chain = true) 20 | @NoArgsConstructor 21 | @ToString 22 | @JsonInclude(value = JsonInclude.Include.NON_NULL) 23 | @JsonIgnoreProperties(ignoreUnknown = true) 24 | public class WalletDto { 25 | 26 | private BigDecimal balance; 27 | private WalletLevel walletLevel; 28 | private String userName; 29 | 30 | } -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/dto/model/user/UserDto.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.dto.model.user; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | import lombok.ToString; 9 | import lombok.experimental.Accessors; 10 | 11 | /** 12 | * Created by Arpit Khandelwal. 13 | */ 14 | @Getter 15 | @Setter 16 | @Accessors(chain = true) 17 | @NoArgsConstructor 18 | @ToString 19 | @JsonInclude(value = JsonInclude.Include.NON_NULL) 20 | @JsonIgnoreProperties(ignoreUnknown = true) 21 | public class UserDto { 22 | private String username; 23 | private String password; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/dto/response/Response.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | import lombok.experimental.Accessors; 9 | 10 | /** 11 | * @author Arpit Khandelwal 12 | */ 13 | @Getter 14 | @Setter 15 | @Accessors(chain = true) 16 | @NoArgsConstructor 17 | @JsonInclude(JsonInclude.Include.NON_NULL) 18 | @JsonIgnoreProperties(ignoreUnknown = true) 19 | public class Response { 20 | 21 | private Status status; 22 | private T payload; 23 | private Object errors; 24 | private Object metadata; 25 | 26 | public static Response badRequest() { 27 | Response response = new Response<>(); 28 | response.setStatus(Status.BAD_REQUEST); 29 | return response; 30 | } 31 | 32 | public static Response ok() { 33 | Response response = new Response<>(); 34 | response.setStatus(Status.OK); 35 | return response; 36 | } 37 | 38 | public static Response unauthorized() { 39 | Response response = new Response<>(); 40 | response.setStatus(Status.UNAUTHORIZED); 41 | return response; 42 | } 43 | 44 | public static Response validationException() { 45 | Response response = new Response<>(); 46 | response.setStatus(Status.VALIDATION_EXCEPTION); 47 | return response; 48 | } 49 | 50 | public static Response wrongCredentials() { 51 | Response response = new Response<>(); 52 | response.setStatus(Status.WRONG_CREDENTIALS); 53 | return response; 54 | } 55 | 56 | public static Response accessDenied() { 57 | Response response = new Response<>(); 58 | response.setStatus(Status.ACCESS_DENIED); 59 | return response; 60 | } 61 | 62 | public static Response exception() { 63 | Response response = new Response<>(); 64 | response.setStatus(Status.EXCEPTION); 65 | return response; 66 | } 67 | 68 | public static ResponseError addErrorMsgToResponse(Response response, String errorMsg) { 69 | ResponseError error = new ResponseError(); 70 | error.setText(errorMsg); 71 | response.setErrors(error); 72 | return error; 73 | } 74 | 75 | public enum Status { 76 | OK, BAD_REQUEST, UNAUTHORIZED, VALIDATION_EXCEPTION, EXCEPTION, WRONG_CREDENTIALS, ACCESS_DENIED 77 | } 78 | 79 | @Getter 80 | @Accessors(chain = true) 81 | @JsonInclude(JsonInclude.Include.NON_NULL) 82 | @JsonIgnoreProperties(ignoreUnknown = true) 83 | public static class PageMetadata { 84 | private int size; 85 | private long totalElements; 86 | private int totalPages; 87 | private int number; 88 | 89 | public PageMetadata(int size, long totalElements, int totalPages, int number) { 90 | this.size = size; 91 | this.totalElements = totalElements; 92 | this.totalPages = totalPages; 93 | this.number = number; 94 | } 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/dto/response/ResponseError.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | import lombok.experimental.Accessors; 9 | 10 | /** 11 | * Created by Arpit Khandelwal 12 | */ 13 | @Getter 14 | @Setter 15 | @Accessors(chain = true) 16 | @NoArgsConstructor 17 | @JsonInclude(JsonInclude.Include.NON_NULL) 18 | @JsonIgnoreProperties(ignoreUnknown = true) 19 | public class ResponseError { 20 | private String text; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/mapper/FaqMapper.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.mapper; 2 | 3 | 4 | import com.systango.springboard.domain.model.Faq; 5 | import com.systango.springboard.dto.model.FaqDto; 6 | 7 | import java.util.List; 8 | 9 | import static java.util.stream.Collectors.toList; 10 | 11 | /** 12 | * Created by Arpit Khandelwal. 13 | */ 14 | public abstract class FaqMapper { 15 | 16 | public static List mapFaqs(List faqs) { 17 | return faqs.stream().map(FaqMapper::mapFaq).collect(toList()); 18 | } 19 | 20 | public static FaqDto mapFaq(Faq faq) { 21 | return new FaqDto() 22 | .setAnswer(faq.getAnswer()) 23 | .setQuestion(faq.getQuestion()); 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/mapper/WalletMapper.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.mapper; 2 | 3 | 4 | import com.systango.springboard.domain.model.payment.WalletDetails; 5 | import com.systango.springboard.dto.model.payment.WalletDto; 6 | 7 | /** 8 | * @author Arpit Khandelwal 9 | */ 10 | public class WalletMapper { 11 | 12 | public static WalletDto mapWallet(WalletDetails walletDetails) { 13 | return new WalletDto() 14 | .setBalance(walletDetails.getBalance()) 15 | .setUserName(walletDetails.getApplicationUser().getUsername()) 16 | .setWalletLevel(walletDetails.getWalletLevel()); 17 | } 18 | } -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/security/JWTAuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.security; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.systango.springboard.domain.model.user.ApplicationUser; 5 | import io.jsonwebtoken.Claims; 6 | import io.jsonwebtoken.Jwts; 7 | import io.jsonwebtoken.SignatureAlgorithm; 8 | import org.springframework.security.authentication.AuthenticationManager; 9 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 10 | import org.springframework.security.core.Authentication; 11 | import org.springframework.security.core.AuthenticationException; 12 | import org.springframework.security.core.userdetails.User; 13 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 14 | 15 | import javax.servlet.FilterChain; 16 | import javax.servlet.ServletException; 17 | import javax.servlet.http.HttpServletRequest; 18 | import javax.servlet.http.HttpServletResponse; 19 | import java.io.IOException; 20 | import java.util.ArrayList; 21 | import java.util.Date; 22 | import java.util.List; 23 | 24 | import static com.systango.springboard.security.SecurityConstants.*; 25 | 26 | 27 | /** 28 | * Created by Arpit Khandelwal. 29 | */ 30 | public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter { 31 | private AuthenticationManager authenticationManager; 32 | 33 | public JWTAuthenticationFilter(AuthenticationManager authenticationManager) { 34 | this.authenticationManager = authenticationManager; 35 | } 36 | 37 | @Override 38 | public Authentication attemptAuthentication(HttpServletRequest req, 39 | HttpServletResponse res) throws AuthenticationException { 40 | try { 41 | ApplicationUser user; 42 | String username = req.getParameter("username"); 43 | String password = req.getParameter("password"); 44 | // A small hack to test if the request is sent from web form or api json post 45 | if (username != null && password != null) { 46 | // This indicates request was sent from the web view 47 | user = new ApplicationUser().setUsername(username).setPassword(password); 48 | } else { 49 | // This indicates request was sent as JSON data 50 | user = new ObjectMapper().readValue(req.getInputStream(), ApplicationUser.class); 51 | } 52 | 53 | return authenticationManager.authenticate( 54 | new UsernamePasswordAuthenticationToken( 55 | user.getUsername(), 56 | user.getPassword(), 57 | new ArrayList<>()) 58 | ); 59 | } catch (IOException e) { 60 | throw new RuntimeException(e); 61 | } 62 | } 63 | 64 | @Override 65 | protected void successfulAuthentication(HttpServletRequest req, 66 | HttpServletResponse res, 67 | FilterChain chain, 68 | Authentication auth) throws IOException, ServletException { 69 | if (auth.getPrincipal() != null) { 70 | // The Auth Mechanism stores the Username the Principal. 71 | // The username is stored in the Subject field of the Token 72 | User user = (User) auth.getPrincipal(); 73 | String login = user.getUsername(); 74 | if (login != null && login.length() > 0) { 75 | Claims claims = Jwts.claims().setSubject(login); 76 | List roles = new ArrayList<>(); 77 | user.getAuthorities().stream().forEach(authority -> roles.add(authority.getAuthority())); 78 | claims.put("roles", roles); 79 | String token = Jwts.builder() 80 | .setClaims(claims) 81 | .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) 82 | .signWith(SignatureAlgorithm.HS512, SECRET.getBytes()) 83 | .compact(); 84 | res.addHeader(HEADER_STRING, TOKEN_PREFIX + token); 85 | } 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/security/JWTAuthorizationFilter.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.security; 2 | 3 | import io.jsonwebtoken.Claims; 4 | import io.jsonwebtoken.Jwts; 5 | import org.springframework.security.authentication.AuthenticationManager; 6 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 7 | import org.springframework.security.core.GrantedAuthority; 8 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 9 | import org.springframework.security.core.context.SecurityContextHolder; 10 | import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; 11 | 12 | import javax.servlet.FilterChain; 13 | import javax.servlet.ServletException; 14 | import javax.servlet.http.HttpServletRequest; 15 | import javax.servlet.http.HttpServletResponse; 16 | import java.io.IOException; 17 | import java.util.ArrayList; 18 | 19 | import static com.systango.springboard.security.SecurityConstants.*; 20 | 21 | 22 | public class JWTAuthorizationFilter extends BasicAuthenticationFilter { 23 | 24 | public JWTAuthorizationFilter(AuthenticationManager authManager) { 25 | super(authManager); 26 | } 27 | 28 | @Override 29 | protected void doFilterInternal(HttpServletRequest req, 30 | HttpServletResponse res, 31 | FilterChain chain) throws IOException, ServletException { 32 | String header = req.getHeader(HEADER_STRING); 33 | 34 | if (header == null || !header.startsWith(TOKEN_PREFIX)) { 35 | chain.doFilter(req, res); 36 | return; 37 | } 38 | 39 | UsernamePasswordAuthenticationToken authentication = getAuthentication(req); 40 | 41 | SecurityContextHolder.getContext().setAuthentication(authentication); 42 | chain.doFilter(req, res); 43 | } 44 | 45 | private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) { 46 | String token = request.getHeader(HEADER_STRING); 47 | if (token != null) { 48 | // parse the token. 49 | Claims claims = Jwts.parser() 50 | .setSigningKey(SECRET.getBytes()) 51 | .parseClaimsJws(token.replace(TOKEN_PREFIX, "")) 52 | .getBody(); 53 | // Extract the UserName 54 | String user = claims.getSubject(); 55 | // Extract the Roles 56 | @SuppressWarnings("unchecked") 57 | ArrayList roles = (ArrayList) claims.get("roles"); 58 | // Then convert Roles to GrantedAuthority Object for injecting 59 | ArrayList list = new ArrayList<>(); 60 | if (roles != null) { 61 | for (String a : roles) { 62 | GrantedAuthority g = new SimpleGrantedAuthority(a); 63 | list.add(g); 64 | } 65 | } 66 | if (user != null) { 67 | return new UsernamePasswordAuthenticationToken(user, null, list); 68 | } 69 | } 70 | return null; 71 | } 72 | } -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/security/SecurityConstants.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.security; 2 | 3 | /** 4 | * Security Constants 5 | */ 6 | public class SecurityConstants { 7 | public static final String SECRET = "SecretKeyToGenJWTs"; 8 | public static final long EXPIRATION_TIME = 864_000_000; // 10 days 9 | public static final String TOKEN_PREFIX = "Bearer "; 10 | public static final String HEADER_STRING = "Authorization"; 11 | public static final String SIGN_UP_URL = "/v1/user/signup"; 12 | } -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/security/UserDetailsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.security; 2 | 3 | import com.systango.springboard.domain.model.user.ApplicationUser; 4 | import com.systango.springboard.domain.model.user.Role; 5 | import com.systango.springboard.domain.repository.ApplicationUserRepository; 6 | import org.springframework.security.core.GrantedAuthority; 7 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 8 | import org.springframework.security.core.userdetails.UserDetails; 9 | import org.springframework.security.core.userdetails.UserDetailsService; 10 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 11 | import org.springframework.stereotype.Service; 12 | 13 | import java.util.HashSet; 14 | 15 | @Service 16 | public class UserDetailsServiceImpl implements UserDetailsService { 17 | private ApplicationUserRepository applicationUserRepository; 18 | 19 | public UserDetailsServiceImpl(ApplicationUserRepository applicationUserRepository) { 20 | this.applicationUserRepository = applicationUserRepository; 21 | } 22 | 23 | @Override 24 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 25 | ApplicationUser user = applicationUserRepository.findByUsername(username); 26 | if (user == null) { 27 | throw new UsernameNotFoundException(username); 28 | } 29 | HashSet authorities = new HashSet<>(); 30 | if (user.getRoles() != null) { 31 | user.getRoles().stream() 32 | .map(Role::getName) 33 | .map(SimpleGrantedAuthority::new) 34 | .forEach(authorities::add); 35 | } 36 | return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities); 37 | } 38 | } -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/security/WebSecurity.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.security; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.http.HttpMethod; 5 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 6 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 7 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 8 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 9 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 10 | import org.springframework.security.config.http.SessionCreationPolicy; 11 | import org.springframework.security.core.userdetails.UserDetailsService; 12 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 13 | import org.springframework.web.cors.CorsConfiguration; 14 | import org.springframework.web.cors.CorsConfigurationSource; 15 | import org.springframework.web.cors.UrlBasedCorsConfigurationSource; 16 | 17 | import static com.systango.springboard.security.SecurityConstants.SIGN_UP_URL; 18 | 19 | 20 | /** 21 | * Created by Arpit Khandelwal. 22 | */ 23 | @EnableWebSecurity 24 | @EnableGlobalMethodSecurity(prePostEnabled = true) 25 | public class WebSecurity extends WebSecurityConfigurerAdapter { 26 | private UserDetailsService userDetailsService; 27 | private BCryptPasswordEncoder bCryptPasswordEncoder; 28 | 29 | public WebSecurity(UserDetailsService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) { 30 | this.userDetailsService = userDetailsService; 31 | this.bCryptPasswordEncoder = bCryptPasswordEncoder; 32 | } 33 | 34 | @Override 35 | protected void configure(HttpSecurity http) throws Exception { 36 | //@formatter:off 37 | http 38 | .cors() 39 | .and() 40 | .csrf() 41 | .disable() 42 | .authorizeRequests() 43 | //Swagger exceptions 44 | .antMatchers(HttpMethod.GET, "/v2/api-docs", "/configuration/ui", "/configuration/security", "/swagger-ui.html", "/webjars/**", "/swagger-resources/**", "/swagge‌​r-ui.html") 45 | .permitAll() 46 | //Static resources exception 47 | .antMatchers(HttpMethod.GET, "/", "/resources/static/**", "/css/**", "/js/**", "/img/**", "/favicon.ico") 48 | .permitAll() 49 | .antMatchers(HttpMethod.GET, "/manage/**") 50 | .permitAll() 51 | //Sign-up API exception 52 | .antMatchers(HttpMethod.POST, SIGN_UP_URL) 53 | .permitAll() 54 | .anyRequest().authenticated() 55 | .and() 56 | .formLogin() 57 | .loginPage("/login") 58 | .permitAll() 59 | .and() 60 | .logout() 61 | .permitAll() 62 | .and() 63 | .addFilter(new JWTAuthenticationFilter(authenticationManager())) 64 | .addFilter(new JWTAuthorizationFilter(authenticationManager())) 65 | // this disables session creation on Spring Security 66 | .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); 67 | //@formatter:on 68 | } 69 | 70 | @Override 71 | public void configure(AuthenticationManagerBuilder auth) throws Exception { 72 | auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder); 73 | } 74 | 75 | @Bean 76 | CorsConfigurationSource corsConfigurationSource() { 77 | UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); 78 | source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues()); 79 | return source; 80 | } 81 | } -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/service/admin/AdminService.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.service.admin; 2 | 3 | import com.systango.springboard.dto.model.FaqDto; 4 | import com.systango.springboard.service.exception.AdminException; 5 | 6 | /** 7 | * Created by Arpit Khandelwal. 8 | */ 9 | 10 | public interface AdminService { 11 | 12 | /** 13 | * Add a new FAQ to the existing list of those 14 | * 15 | * @param faqDto 16 | * @return confirmation message 17 | */ 18 | String addFaq(FaqDto faqDto) throws AdminException; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/service/admin/AdminServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.service.admin; 2 | 3 | import com.systango.springboard.domain.model.Faq; 4 | import com.systango.springboard.domain.repository.FaqRepository; 5 | import com.systango.springboard.dto.model.FaqDto; 6 | import com.systango.springboard.service.exception.AdminException; 7 | import com.systango.springboard.util.AssertUtils; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.stereotype.Component; 12 | import org.springframework.transaction.annotation.Transactional; 13 | 14 | import java.util.Optional; 15 | 16 | /** 17 | * Created by Arpit Khandelwal. 18 | */ 19 | @Component 20 | public class AdminServiceImpl implements AdminService { 21 | 22 | private static final Logger LOGGER = LoggerFactory.getLogger(AdminServiceImpl.class); 23 | private final FaqRepository faqRepository; 24 | 25 | @Autowired 26 | public AdminServiceImpl(FaqRepository faqRepository) { 27 | this.faqRepository = faqRepository; 28 | } 29 | 30 | @Override 31 | @Transactional 32 | public String addFaq(FaqDto faqDto) throws AdminException { 33 | AssertUtils.notNull(faqDto, faqDto.getQuestion()); 34 | 35 | Optional faq = Optional.ofNullable(faqRepository.findByQuestion(faqDto.getQuestion())); 36 | if (faq.isPresent()) { 37 | LOGGER.debug("FAQ with Question:'{}' and Answer: '{}' already exists", faqDto.getQuestion(), faqDto.getAnswer()); 38 | throw new AdminException(String.format("FAQ with Question:'%s' and Answer: '%s' already exists", faqDto.getQuestion(), faqDto.getAnswer())); 39 | } else { 40 | //persist the new faq 41 | Faq newFaq = new Faq() 42 | .setQuestion(faqDto.getQuestion()) 43 | .setAnswer(faqDto.getAnswer()); 44 | faqRepository.save(newFaq); 45 | } 46 | return "FAQ added successfully"; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/service/exception/AdminException.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.service.exception; 2 | 3 | /** 4 | * Created by Arpit Khandelwal. 5 | */ 6 | public class AdminException extends Exception { 7 | 8 | public AdminException(String message) { 9 | super(message); 10 | } 11 | 12 | public AdminException(Throwable throwable) { 13 | super(throwable); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/service/exception/PaymentException.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.service.exception; 2 | 3 | /** 4 | * Created by Arpit Khandelwal. 5 | */ 6 | public class PaymentException extends Exception { 7 | public PaymentException(String message) { 8 | super(message); 9 | } 10 | 11 | public PaymentException(Throwable throwable) { 12 | super(throwable); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/service/exception/UserException.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.service.exception; 2 | 3 | /** 4 | * Created by Arpit Khandelwal. 5 | */ 6 | public class UserException extends Exception { 7 | 8 | public UserException(String message) { 9 | super(message); 10 | } 11 | 12 | public UserException(Throwable throwable) { 13 | super(throwable); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/service/payment/PaymentService.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.service.payment; 2 | 3 | import com.systango.springboard.domain.model.payment.WalletDetails; 4 | import com.systango.springboard.dto.model.payment.WalletDto; 5 | import com.systango.springboard.service.exception.PaymentException; 6 | 7 | /** 8 | * Created by Arpit Khandelwal. 9 | */ 10 | public interface PaymentService { 11 | 12 | /** 13 | * Get a user's wallet details. 14 | * 15 | * @param userName 16 | * @return 17 | * @throws PaymentException 18 | */ 19 | WalletDetails getWalletDetails(String userName) throws PaymentException; 20 | 21 | /** 22 | * Create a new wallet for the user with given id 23 | * 24 | * @param walletDto 25 | * @return 26 | * @throws PaymentException 27 | */ 28 | WalletDetails createUsersWallet(WalletDto walletDto) throws PaymentException; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/service/payment/PaymentServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.service.payment; 2 | 3 | import com.systango.springboard.domain.model.payment.WalletDetails; 4 | import com.systango.springboard.domain.model.user.ApplicationUser; 5 | import com.systango.springboard.domain.repository.ApplicationUserRepository; 6 | import com.systango.springboard.domain.repository.WalletRepository; 7 | import com.systango.springboard.dto.model.payment.WalletDto; 8 | import com.systango.springboard.service.exception.PaymentException; 9 | import com.systango.springboard.util.AssertUtils; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.stereotype.Component; 14 | import org.springframework.transaction.annotation.Transactional; 15 | 16 | import java.util.Optional; 17 | 18 | /** 19 | * Created by Arpit Khandelwal. 20 | */ 21 | @Component 22 | public class PaymentServiceImpl implements PaymentService { 23 | 24 | private static final Logger LOGGER = LoggerFactory.getLogger(PaymentServiceImpl.class); 25 | 26 | private final ApplicationUserRepository userRepository; 27 | 28 | private final WalletRepository walletRepository; 29 | 30 | @Autowired 31 | public PaymentServiceImpl(ApplicationUserRepository userRepository, WalletRepository walletRepository) { 32 | this.userRepository = userRepository; 33 | this.walletRepository = walletRepository; 34 | } 35 | 36 | /** 37 | * Returns the wallet details of specified user 38 | * 39 | * @param userName 40 | * @return 41 | * @throws PaymentException 42 | */ 43 | @Override 44 | @Transactional 45 | public WalletDetails getWalletDetails(String userName) throws PaymentException { 46 | Optional user = Optional.ofNullable(userRepository.findByUsername(userName)); 47 | if (user.isPresent()) { 48 | Optional wallet = Optional.ofNullable(walletRepository.findByApplicationUser_Id(user.get().getId())); 49 | if (wallet.isPresent()) { 50 | return wallet.get(); 51 | } else { 52 | throw new PaymentException(String.format("Requested user - '%s' doesn't have a wallet yet", userName)); 53 | } 54 | } else { 55 | throw new PaymentException(String.format("Requested user - '%s'doesn 't exist.", userName)); 56 | } 57 | } 58 | 59 | /** 60 | * Create a new User Wallet 61 | * 62 | * @param walletDto 63 | * @return 64 | * @throws PaymentException 65 | */ 66 | @Override 67 | @Transactional 68 | public WalletDetails createUsersWallet(WalletDto walletDto) throws PaymentException { 69 | AssertUtils.notNull(walletDto); 70 | Optional user = Optional.ofNullable(userRepository.findByUsername(walletDto.getUserName())); 71 | if (user.isPresent()) { 72 | WalletDetails walletDetails = new WalletDetails() 73 | .setWalletLevel(walletDto.getWalletLevel()) 74 | .setApplicationUser(user.get()); 75 | user.get().setWalletDetails(walletDetails); 76 | walletRepository.save(walletDetails); 77 | return walletDetails; 78 | } else { 79 | LOGGER.debug("User with username :'{}' doesn't exist", walletDto.getUserName()); 80 | throw new PaymentException(String.format("User with username :'%s' doesn't exist", walletDto.getUserName())); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/service/user/UserService.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.service.user; 2 | 3 | import com.systango.springboard.domain.model.payment.WalletDetails; 4 | import com.systango.springboard.domain.model.payment.WalletLevel; 5 | import com.systango.springboard.dto.model.user.UserDto; 6 | import com.systango.springboard.service.exception.PaymentException; 7 | import com.systango.springboard.service.exception.UserException; 8 | 9 | /** 10 | * Created by Arpit Khandelwal. 11 | */ 12 | public interface UserService { 13 | /** 14 | * Service method to add a new user. 15 | * 16 | * @param userDto 17 | * @return 18 | * @throws UserException 19 | */ 20 | String addUser(UserDto userDto) throws UserException; 21 | 22 | /** 23 | * Create a new wallet for the given user. 24 | * 25 | * @param userDto 26 | * @param walletLevel 27 | * @return 28 | */ 29 | 30 | WalletDetails createUserWallet(UserDto userDto, WalletLevel walletLevel) throws PaymentException; 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/service/user/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.service.user; 2 | 3 | import com.systango.springboard.domain.model.payment.WalletDetails; 4 | import com.systango.springboard.domain.model.payment.WalletLevel; 5 | import com.systango.springboard.domain.model.user.ApplicationUser; 6 | import com.systango.springboard.domain.repository.ApplicationUserRepository; 7 | import com.systango.springboard.dto.model.payment.WalletDto; 8 | import com.systango.springboard.dto.model.user.UserDto; 9 | import com.systango.springboard.service.exception.PaymentException; 10 | import com.systango.springboard.service.exception.UserException; 11 | import com.systango.springboard.service.payment.PaymentService; 12 | import com.systango.springboard.util.AssertUtils; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.stereotype.Component; 15 | import org.springframework.transaction.annotation.Transactional; 16 | 17 | import java.util.Optional; 18 | 19 | /** 20 | * Created by Arpit Khandelwal. 21 | */ 22 | @Component 23 | public class UserServiceImpl implements UserService { 24 | 25 | private final ApplicationUserRepository userRepository; 26 | 27 | private PaymentService paymentService; 28 | 29 | /** 30 | * @param userRepository 31 | */ 32 | @Autowired 33 | public UserServiceImpl(ApplicationUserRepository userRepository) { 34 | this.userRepository = userRepository; 35 | } 36 | 37 | @Override 38 | @Transactional 39 | public String addUser(UserDto userDto) throws UserException { 40 | AssertUtils.notNull(userDto, userDto.getUsername(), userDto.getPassword()); 41 | Optional user = Optional.ofNullable(userRepository.findByUsername(userDto.getUsername())); 42 | 43 | if (user.isPresent()) { 44 | throw new UserException(String.format("Requested user - '%s' already exists.", userDto.getUsername())); 45 | } else { 46 | ApplicationUser appUser = new ApplicationUser() 47 | .setUsername(userDto.getUsername()) 48 | .setPassword(userDto.getPassword()); 49 | userRepository.save(appUser); 50 | } 51 | return null; 52 | } 53 | 54 | @Override 55 | @Transactional 56 | public WalletDetails createUserWallet(UserDto userDto, WalletLevel level) throws PaymentException { 57 | AssertUtils.notNull(userDto, userDto.getUsername(), userDto.getPassword()); 58 | Optional user = Optional.ofNullable(userRepository.findByUsername(userDto.getUsername())); 59 | if (user.isPresent()) { 60 | WalletDto walletDto = new WalletDto() 61 | .setWalletLevel(level) 62 | .setUserName(userDto.getUsername()); 63 | return paymentService.createUsersWallet(walletDto); 64 | } 65 | return null; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/util/AssertUtils.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.util; 2 | 3 | import org.springframework.util.Assert; 4 | 5 | import java.math.BigDecimal; 6 | 7 | /** 8 | * @author Arpit Khandelwal 9 | */ 10 | public class AssertUtils { 11 | 12 | private AssertUtils() { 13 | 14 | } 15 | 16 | public static void notNull(Object... objects) { 17 | for (Object object : objects) { 18 | Assert.notNull(object, "argument can not be null"); 19 | } 20 | } 21 | 22 | public static void positive(BigDecimal value) { 23 | Assert.isTrue(value.signum() == 1, "argument should be a positive value"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/systango/springboard/util/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard.util; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import springfox.documentation.builders.ApiInfoBuilder; 6 | import springfox.documentation.builders.PathSelectors; 7 | import springfox.documentation.builders.RequestHandlerSelectors; 8 | import springfox.documentation.service.ApiInfo; 9 | import springfox.documentation.service.ApiKey; 10 | import springfox.documentation.service.Contact; 11 | import springfox.documentation.spi.DocumentationType; 12 | import springfox.documentation.spring.web.plugins.Docket; 13 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 14 | 15 | import java.util.Arrays; 16 | 17 | /** 18 | * Created by Arpit Khandelwal. 19 | */ 20 | @Configuration 21 | @EnableSwagger2 22 | public class SwaggerConfig { 23 | @Bean 24 | public Docket api() { 25 | return new Docket(DocumentationType.SWAGGER_2) 26 | .select() 27 | .apis(RequestHandlerSelectors.basePackage("com.systango.springboard.api")) 28 | .paths(PathSelectors.any()) 29 | .build() 30 | .apiInfo(apiInfo()) 31 | .securitySchemes(Arrays.asList(apiKey())); 32 | } 33 | 34 | private ApiInfo apiInfo() { 35 | return new ApiInfoBuilder().title("SPRINGBOARD APIs") 36 | .description("The REST API documentation for springboard project.").termsOfServiceUrl("") 37 | .contact(new Contact("Pallav Boonlia", "https://www.systango.com/contact", "pallav@systango.com")) 38 | .version("1.0.1") 39 | .build(); 40 | } 41 | 42 | private ApiKey apiKey() { 43 | return new ApiKey("authkey", "Authorization", "header"); 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | #Server configurations 2 | server.port=8090 3 | logging.path=logs 4 | #Database connection settings 5 | spring.datasource.driverClassName=org.postgresql.Driver 6 | spring.datasource.url=jdbc:postgresql://${db_hostname:localhost}:${db_port:5432}/springboard_db 7 | spring.datasource.username=${SPRING_DATASOURCE_USERNAME:postgres} 8 | spring.datasource.password=${SPRING_DATASOURCE_PASSWORD:postgres} 9 | spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true 10 | #Database initialization settings 11 | # If you wish to only update the database and NOT import data from data.sql then use the following settings 12 | #spring.jpa.hibernate.ddl-auto=update 13 | #spring.datasource.initialize=false 14 | # If you wish to create the database and import sample data then use the following settings 15 | #spring.jpa.hibernate.ddl-auto=create 16 | #spring.datasource.initialize=true 17 | # Management settings 18 | management.security.enabled=false 19 | management.context-path=/manage 20 | -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | _____ _ _ _ 2 | / ____| (_) | | | | 3 | | (___ _ __ _ __ _ _ __ __ _ | |__ ___ __ _ _ __ __| | 4 | \___ \ | '_ \ | '__| | | | '_ \ / _` | | '_ \ / _ \ / _` | | '__| / _` | 5 | ____) | | |_) | | | | | | | | | | (_| | | |_) | | (_) | | (_| | | | | (_| | 6 | |_____/ | .__/ |_| |_| |_| |_| \__, | |_.__/ \___/ \__,_| |_| \__,_| 7 | | | __/ | 8 | |_| |___/ 9 | 10 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V1.1__Init_DB.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE application_user 2 | ( 3 | id serial, 4 | create_timestamp timestamp without time zone, 5 | last_edit_timestamp timestamp without time zone, 6 | email character varying(255), 7 | first_name character varying(255), 8 | last_name character varying(255), 9 | password character varying(255), 10 | phone_number character varying(255), 11 | username character varying(255), 12 | CONSTRAINT application_user_pkey PRIMARY KEY (id) 13 | ); 14 | 15 | CREATE TABLE role 16 | ( 17 | id serial, 18 | create_timestamp timestamp without time zone, 19 | last_edit_timestamp timestamp without time zone, 20 | name character varying(255), 21 | CONSTRAINT role_pkey PRIMARY KEY (id) 22 | ); 23 | 24 | CREATE TABLE user_roles 25 | ( 26 | user_id bigint NOT NULL, 27 | role_id bigint NOT NULL, 28 | CONSTRAINT user_roles_userid_fk FOREIGN KEY (user_id) 29 | REFERENCES public.application_user (id) MATCH SIMPLE 30 | ON UPDATE NO ACTION ON DELETE NO ACTION, 31 | CONSTRAINT user_roles_roleid_fk FOREIGN KEY (role_id) 32 | REFERENCES public.role (id) MATCH SIMPLE 33 | ON UPDATE NO ACTION ON DELETE NO ACTION 34 | ); 35 | 36 | CREATE TABLE settings 37 | ( 38 | id serial, 39 | create_timestamp timestamp without time zone, 40 | last_edit_timestamp timestamp without time zone, 41 | setting_key character varying(255), 42 | setting_value character varying(255), 43 | CONSTRAINT settings_pkey PRIMARY KEY (id) 44 | ); 45 | 46 | INSERT INTO APPLICATION_USER (username, password, email, first_name, last_name, phone_number) 47 | VALUES 48 | ('admin', '$2a$10$RyICQ69.sgW0JCsdSvwmu.iYuZ2vNyr5X7UpQjhayj.6WRL2KqnMO', 49 | 'admin@sprinboard.com', 'super', 'admin', '9425094250'); 50 | INSERT INTO APPLICATION_USER (username, password, email, first_name, last_name, phone_number) 51 | VALUES ('user', '$2a$10$RyICQ69.sgW0JCsdSvwmu.iYuZ2vNyr5X7UpQjhayj.6WRL2KqnMO', 52 | 'user@springboard.com', 'normal', 'user', '9425094251'); 53 | 54 | --ROLES 55 | INSERT INTO ROLE (name) 56 | VALUES ('ROLE_ADMIN'); 57 | INSERT INTO ROLE (name) 58 | VALUES ('ROLE_USER'); 59 | 60 | --USER_ROLES 61 | INSERT INTO USER_ROLES (user_id, role_id) 62 | VALUES (1, 1); 63 | INSERT INTO USER_ROLES (user_id, role_id) 64 | VALUES (2, 2); 65 | 66 | --SETTINGS 67 | --About 68 | INSERT INTO SETTINGS (setting_key, setting_value) 69 | VALUES ('about', 'Springboard IS a Spring Boot starter - kit'); 70 | 71 | --Terms 72 | INSERT INTO SETTINGS (setting_key, setting_value) 73 | VALUES ('terms_and_conditions', 'This is an OPEN - SOURCE project.'); 74 | 75 | --Privacy policy 76 | INSERT INTO SETTINGS (setting_key, setting_value) 77 | VALUES ('privacy_policy', ' NO terms AND conditions APPLY AT this TIME '); 78 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V1.2__Init_DB.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE faq 2 | ( 3 | id serial NOT NULL, 4 | create_timestamp timestamp without time zone, 5 | last_edit_timestamp timestamp without time zone, 6 | answer character varying(255), 7 | question character varying(255), 8 | CONSTRAINT faq_pkey PRIMARY KEY (id) 9 | ); 10 | 11 | CREATE TABLE wallet_details 12 | ( 13 | id serial NOT NULL, 14 | create_timestamp timestamp without time zone, 15 | last_edit_timestamp timestamp without time zone, 16 | wallet_level character varying(255), 17 | application_user_id bigint, 18 | CONSTRAINT wallet_details_pkey PRIMARY KEY (id), 19 | CONSTRAINT wallet_details_userid_fk FOREIGN KEY (application_user_id) 20 | REFERENCES public.application_user (id) MATCH SIMPLE 21 | ON UPDATE NO ACTION ON DELETE NO ACTION 22 | ); -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %d{dd-MM-yyyy HH:mm:ss} %magenta([%thread]) %highlight(%-5level) %logger{36}.%M - %msg%n 7 | 8 | 9 | 10 | 11 | 12 | ${LOG_PATH}/log.log 13 | 14 | %d{dd-MM-yyyy HH:mm:ss} [%thread] %-5level %logger{36}.%M - %msg%n 15 | 16 | 17 | ${LOG_PATH}/log_%i.log 18 | 2 19 | 3 20 | 21 | 22 | 10KB 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/main/resources/static/css/_custom.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Created by Kroplet (https://www.kroplet.com) 3 | * The easiest way to create Bootstrap 4 themes. 4 | */ 5 | 6 | /*Google fonts*/ 7 | @import url('https://fonts.googleapis.com/css?family=Rubik:400,500,700'); 8 | 9 | html, 10 | body { 11 | width: 100%; 12 | height: 100%; 13 | } 14 | body{ 15 | font-family: 'Rubik', sans-serif; 16 | font-size: 16px; 17 | background-color: #FAFAFA; 18 | } 19 | h1,h2,h3,h4,h5,h6{ 20 | font-weight: 700; 21 | } 22 | b, strong{ 23 | font-weight: 500; 24 | } 25 | .brand-logo{ 26 | width: 180px; 27 | } 28 | .navbar{ 29 | min-height: 80px; 30 | } 31 | .navbar-nav .nav-item .nav-link{ 32 | font-weight: 500; 33 | } 34 | .navbar-brand { 35 | font-weight: 700; 36 | } 37 | .navbar-inverse .navbar-nav .nav-link, .navbar .navbar-brand, .navbar-toggler span { 38 | color: rgba(255,255,255,.8); 39 | } 40 | .navbar .nav-item .active, .navbar .nav-link:hover,.navbar .nav-link:focus, .navbar .navbar-brand:hover{ 41 | color: #fff !important; 42 | } 43 | .sticky-navigation{ 44 | top: 0px; 45 | background-color: transparent; 46 | } 47 | .hero{ 48 | height: 100vh; 49 | min-height: 500px; 50 | padding: 100px 0; 51 | } 52 | .hero .brand{ 53 | margin-top: 100px; 54 | } 55 | @media (max-width: 768px){ 56 | .hero .brand { 57 | margin-top: 0px; 58 | } 59 | } 60 | .store-img 61 | { 62 | width: 170px; 63 | } 64 | .hero h1{ 65 | font-weight: 100; 66 | } 67 | .hero h1 b{ 68 | font-weight: 700; 69 | } 70 | a { 71 | color: #d81b60; 72 | -webkit-transition: all .35s; 73 | -moz-transition: all .35s; 74 | transition: all .35s; 75 | } 76 | a:hover, 77 | a:focus { 78 | color: #d81b60 !important; 79 | outline: 0; 80 | text-decoration: none !important; 81 | } 82 | section{ 83 | padding: 80px 0px; 84 | } 85 | .img-team{ 86 | width: 150px; 87 | box-shadow: 3px 2px 3px rgba(0, 0, 0, 0.15); 88 | -webkit-transition: 350ms ease all; 89 | transition: 350ms ease all; 90 | } 91 | .img-team:hover{ 92 | box-shadow: 6px 6px 25px rgba(0, 0, 0, 0.15); 93 | } 94 | .team h5{ 95 | padding: 35px 0 15px 0; 96 | font-weight: 500; 97 | } 98 | .team p{ 99 | color: #999; 100 | } 101 | .card-outline-primary{ 102 | border: solid 1px #d81b60; 103 | } 104 | .small-xl{ 105 | font-size: 90%; 106 | } 107 | .small-xs{ 108 | font-size: 70%; 109 | } 110 | .badge { 111 | color: #fff; 112 | } 113 | /*=============================================== 114 | * Background Colors 115 | ================================================*/ 116 | .bg-texture{ 117 | background: transparent -webkit-radial-gradient(circle farthest-side at right bottom, #ec407a 5%, #e91e63 25%, #d81b60 80%, #c2185b 98%) repeat scroll 0% 0%; 118 | background: transparent -o-radial-gradient(circle farthest-side at right bottom, #ec407a 5%, #e91e63 25%, #d81b60 80%, #c2185b 98%) repeat scroll 0% 0%; 119 | background: transparent -moz-radial-gradient(circle farthest-side at right bottom, #ec407a 5%, #e91e63 25%, #d81b60 80%, #c2185b 98%) repeat scroll 0% 0%; 120 | background: transparent radial-gradient(circle farthest-side at right bottom, #ec407a 5%, #e91e63 25%, #d81b60 80%, #c2185b 98%) repeat scroll 0% 0%; 121 | } 122 | .bg-texture-collage{ 123 | background:-webkit-linear-gradient(-45deg, #ffb300 33%, transparent 0%), 124 | linear-gradient(-45deg, #d81b60 67%, transparent 33%), 125 | linear-gradient(-45deg, #ffb300 100%, transparent 67%); 126 | background:-moz-linear-gradient(-45deg, #ffb300 33%, transparent 0%), 127 | linear-gradient(-45deg, #d81b60 67%, transparent 33%), 128 | linear-gradient(-45deg, #ffb300 100%, transparent 67%); 129 | background:linear-gradient(-45deg, #ec407a 33%, transparent 0%), 130 | linear-gradient(-45deg, #e91e63 67%, transparent 33%), 131 | linear-gradient(-45deg, #ec407a 100%, transparent 67%); 132 | } 133 | .bg-alt { 134 | background-color: #fff; 135 | } 136 | .bg-faded{ 137 | background-color: #FAFAFA; 138 | } 139 | .bg-primary{ 140 | background-color: #d81b60 !important; 141 | } 142 | .bg-footer{ 143 | background-color: #171717; 144 | } 145 | /*=============================================== 146 | * Text Colors 147 | ================================================*/ 148 | .text-faded { 149 | color: #FAFAFA; 150 | } 151 | .text-dark { 152 | color: #37474F; 153 | } 154 | .text-muted{ 155 | color: #999 !important; 156 | } 157 | .text-white { 158 | color: #fff; 159 | } 160 | .text-primary { 161 | color: #d81b60 !important; 162 | } 163 | .text-primary-light{ 164 | color: #ff80ab; 165 | } 166 | 167 | /*=============================================== 168 | * Icon Sizes 169 | ================================================*/ 170 | .icon-lg { 171 | font-size: 60px; 172 | line-height: 18px; 173 | } 174 | .icon-md { 175 | font-size: 50px; 176 | line-height: 14px; 177 | } 178 | .icon-sm { 179 | font-size: 30px; 180 | line-height: 14px; 181 | } 182 | /*=============================================== 183 | * Colored Buttons 184 | ================================================*/ 185 | .btn{ 186 | box-shadow: 3px 2px 3px rgba(0, 0, 0, 0.15); 187 | -webkit-transition: 350ms ease all; 188 | transition: 350ms ease all; 189 | text-transform: uppercase; 190 | font-weight: 500; 191 | padding: .6rem 1.5rem; 192 | } 193 | .btn:hover, .btn:focus { 194 | box-shadow: 6px 6px 25px rgba(0, 0, 0, 0.15); 195 | } 196 | .btn-white { 197 | color: #ff4081 !important; 198 | background-color: #fff; 199 | border-color: #fff; 200 | } 201 | .btn-white:hover{ 202 | color: #ff4081; 203 | } 204 | .btn-primary{ 205 | background-color: #ff4081 !important; 206 | border-color: #ff4081 !important; 207 | } 208 | .btn-primary:hover, .btn-primary:focus{ 209 | color: #fff !important; 210 | } 211 | .btn-radius{ 212 | border-radius: 50px; 213 | } 214 | /*=============================================== 215 | * Borders Utilities 216 | ================================================*/ 217 | .border-none{ 218 | border: none !important; 219 | border-color: transparent !important; 220 | } 221 | .border-all{ 222 | border: 1px solid #DEE5E5 !important; 223 | } 224 | .border-left{ 225 | border-left: 1px solid #DEE5E5 !important; 226 | } 227 | .border-right{ 228 | border-right: 1px solid #DEE5E5 !important; 229 | } 230 | .border-top{ 231 | border-top: 1px solid #DEE5E5 !important; 232 | } 233 | .border-bottom{ 234 | border-bottom: 1px solid #DEE5E5 !important; 235 | } 236 | /*=============================================== 237 | * Social Icons 238 | ================================================*/ 239 | .text-twitter-alt, .text-facebook-alt, .text-linkedin-alt, .text-google-alt, .text-github-alt{ 240 | color:#d81b60; 241 | } 242 | .text-twitter, .text-twitter-alt:hover{ 243 | color:#00aced; 244 | } 245 | .text-facebook, .text-facebook-alt:hover{ 246 | color: #3b5998; 247 | } 248 | .text-google, .text-google-alt:hover{ 249 | color: #dd4b39; 250 | } 251 | .text-linkedin, .text-linkedin-alt:hover{ 252 | color: #007bb6; 253 | } 254 | .text-github, .text-github-alt:hover{ 255 | color: #000000; 256 | } 257 | /*=============================================== 258 | * Cards 259 | ================================================*/ 260 | .card{ 261 | border: none; 262 | margin-bottom: 30px; 263 | } 264 | .card .card-body .icon-box{ 265 | padding: 20px 0; 266 | color: #d81b60; 267 | } 268 | .card .card-body h6{ 269 | padding-bottom: 15px; 270 | font-size: 1.2rem; 271 | font-weight: 500; 272 | } 273 | .card:hover{ 274 | box-shadow: 6px 6px 25px rgba(0, 0, 0, 0.15); 275 | } 276 | /* 277 | Pricing tables & lists 278 | */ 279 | .pricing-list{ 280 | padding-top: 25px; 281 | padding-bottom: 25px; 282 | } 283 | .pricing-list li { 284 | font-size: 16px; 285 | font-weight: normal; 286 | margin-bottom: 15px; 287 | } 288 | .list-default{ 289 | list-style: none; 290 | padding-left: 25px; 291 | } 292 | .list-default>li { 293 | position: relative; 294 | padding: 6px 0; 295 | line-height: 1.55em; 296 | font-size: 0.94rem; 297 | } 298 | .list-default>li:before { 299 | content: "\f21b"; 300 | position: absolute; 301 | left: -25px; 302 | top: 8px; 303 | font-size: 10px; 304 | color: #d81b60; 305 | font-family: "Ionicons"; 306 | } 307 | ::selection { 308 | color: #fff; 309 | text-shadow: none; 310 | background: #d81b60; 311 | } -------------------------------------------------------------------------------- /src/main/resources/static/css/_variables.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Created by Kroplet (https://www.kroplet.com) 3 | * The easiest way to create Bootstrap 4 themes. 4 | */ 5 | 6 | // 7 | //Colors 8 | // 9 | 10 | // Base Colors 11 | 12 | $white: #ffffff; 13 | $gray-100: #f8f9fa; 14 | $gray-200: #e9ecef; 15 | $gray-300: #dee2e6; 16 | $gray-400: #ced4da; 17 | $gray-500: #adb5bd; 18 | $gray-600: #868e96; 19 | $gray-700: #495057; 20 | $gray-800: #343a40; 21 | $gray-900: #212529; 22 | $black: #000000; 23 | $blue: #007bff; 24 | $indigo: #6610f2; 25 | $purple: #6f42c1; 26 | $pink: #e83e8c; 27 | $red: #dc3545; 28 | $orange: #fd7e14; 29 | $yellow: #ffc107; 30 | $green: #28a745; 31 | $teal: #20c997; 32 | $cyan: #17a2b8; 33 | 34 | 35 | // Theme Colors 36 | 37 | $primary: $blue; 38 | $secondary: $gray-600; 39 | $success: $green; 40 | $info: $cyan; 41 | $warning: $yellow; 42 | $danger: $red; 43 | $light: $gray-100; 44 | $dark: $gray-800; 45 | 46 | 47 | $theme-colors: (); 48 | $theme-colors: map-merge(( 49 | "primary": $primary, 50 | "secondary": $secondary, 51 | "success": $success, 52 | "info": $info, 53 | "warning": $warning, 54 | "danger": $danger, 55 | "light": $light, 56 | "dark": $dark, 57 | ), $theme-colors); 58 | $theme-color-interval: 8%; 59 | 60 | 61 | // 62 | //Global 63 | // 64 | 65 | $enable-caret: true; 66 | $enable-rounded: true; 67 | $enable-shadows: false; 68 | $enable-gradients: false; 69 | $enable-transitions: true; 70 | $enable-hover-media-query: false; 71 | $enable-grid-classes: true; 72 | $enable-print-styles: true; 73 | 74 | 75 | // 76 | //Spacing 77 | // 78 | 79 | $spacer: 1rem; 80 | $spacers: (0: 0, 1: ($spacer * .25), 2: ($spacer * .5), 3: $spacer, 4: ($spacer * 1.5), 5: ($spacer * 3)); 81 | $sizes: (25: 25%, 50: 50%, 75: 75%, 100: 100%); 82 | 83 | 84 | // 85 | //Body 86 | // 87 | 88 | $body-bg: $white; 89 | $body-color: $gray-900; 90 | 91 | 92 | // 93 | //Links 94 | // 95 | 96 | $link-color: theme-color("primary"); 97 | $link-decoration: none; 98 | $link-hover-color: darken($link-color, 15%); 99 | $link-hover-decoration: underline; 100 | 101 | 102 | // 103 | //Paragraphs 104 | // 105 | 106 | $paragraph-margin-bottom: 1rem; 107 | 108 | 109 | // 110 | //GridBreakpoints 111 | // 112 | 113 | $grid-breakpoints: (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px); 114 | 115 | 116 | // 117 | //GridContainers 118 | // 119 | 120 | $container-max-widths: (sm: 540px, md: 720px, lg: 960px, xl: 1140px); 121 | 122 | 123 | // 124 | //GridColumns 125 | // 126 | 127 | $grid-columns: 12; 128 | $grid-gutter-width: 30px; 129 | 130 | 131 | // 132 | //Components 133 | // 134 | 135 | $line-height-lg: 1.5; 136 | $line-height-sm: 1.5; 137 | $border-width: 1px; 138 | $border-color: $gray-200; 139 | $border-radius: .25rem; 140 | $border-radius-lg: .3rem; 141 | $border-radius-sm: .2rem; 142 | $component-active-color: $white; 143 | $component-active-bg: theme-color("primary"); 144 | $caret-width: .3em; 145 | $transition-base: all .2s ease-in-out; 146 | $transition-fade: opacity .15s linear; 147 | $transition-collapse: height .35s ease; 148 | 149 | 150 | // 151 | //Fonts 152 | // 153 | 154 | $font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 155 | $font-family-monospace: "SFMono-Regular", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 156 | $font-family-base: $font-family-sans-serif; 157 | $font-size-base: 1rem; 158 | $font-size-lg: ($font-size-base * 1.25); 159 | $font-size-sm: ($font-size-base * .875); 160 | $font-weight-light: 300; 161 | $font-weight-normal: 400; 162 | $font-weight-bold: 700; 163 | $font-weight-base: $font-weight-normal; 164 | $line-height-base: 1.5; 165 | $h1-font-size: $font-size-base * 2.5; 166 | $h2-font-size: $font-size-base * 2; 167 | $h3-font-size: $font-size-base * 1.75; 168 | $h4-font-size: $font-size-base * 1.5; 169 | $h5-font-size: $font-size-base * 1.25; 170 | $h6-font-size: $font-size-base; 171 | $headings-margin-bottom: ($spacer / 2); 172 | $headings-font-family: inherit; 173 | $headings-font-weight: 500; 174 | $headings-line-height: 1.2; 175 | $headings-color: inherit; 176 | $display1-size: 6rem; 177 | $display2-size: 5.5rem; 178 | $display3-size: 4.5rem; 179 | $display4-size: 3.5rem; 180 | $display1-weight: 300; 181 | $display2-weight: 300; 182 | $display3-weight: 300; 183 | $display4-weight: 300; 184 | $display-line-height: $headings-line-height; 185 | $lead-font-size: ($font-size-base * 1.25); 186 | $lead-font-weight: 300; 187 | $small-font-size: 80%; 188 | $text-muted: $gray-600; 189 | $blockquote-small-color: $gray-600; 190 | $blockquote-font-size: ($font-size-base * 1.25); 191 | $hr-border-color: rgba($black,.1); 192 | $hr-border-width: $border-width; 193 | $mark-padding: .2em; 194 | $dt-font-weight: $font-weight-bold; 195 | $kbd-box-shadow: inset 0 -.1rem 0 rgba($black,.25); 196 | $nested-kbd-font-weight: $font-weight-bold; 197 | $list-inline-padding: 5px; 198 | $mark-bg: #fcf8e3; 199 | 200 | 201 | // 202 | //Tables 203 | // 204 | 205 | $table-cell-padding: .75rem; 206 | $table-cell-padding-sm: .3rem; 207 | $table-bg: transparent; 208 | $table-accent-bg: rgba($black,.05); 209 | $table-hover-bg: rgba($black,.075); 210 | $table-active-bg: $table-hover-bg; 211 | $table-border-width: $border-width; 212 | $table-border-color: $gray-200; 213 | $table-head-bg: $gray-200; 214 | $table-head-color: $gray-700; 215 | $table-dark-bg: $gray-900; 216 | $table-dark-accent-bg: rgba($white, .05); 217 | $table-dark-hover-bg: rgba($white, .075); 218 | $table-dark-border-color: lighten($gray-900, 7.5%); 219 | $table-dark-color: $body-bg; 220 | 221 | 222 | // 223 | //Buttons 224 | // 225 | 226 | $input-btn-padding-y: .375rem; 227 | $input-btn-padding-x: .75rem; 228 | $input-btn-line-height: $line-height-base; 229 | $input-btn-focus-width: .2rem; 230 | $input-btn-focus-color: rgba(theme-color("primary"), .25); 231 | $input-btn-focus-box-shadow: 0 0 0 $input-btn-focus-width $input-btn-focus-color; 232 | $input-btn-padding-y-sm: .25rem; 233 | $input-btn-padding-x-sm: .5rem; 234 | $input-btn-line-height-sm: $line-height-sm; 235 | $input-btn-padding-y-lg: .5rem; 236 | $input-btn-padding-x-lg: 1rem; 237 | $input-btn-line-height-lg: $line-height-lg; 238 | $btn-font-weight: $font-weight-normal; 239 | $btn-box-shadow: inset 0 1px 0 rgba($white,.15), 0 1px 1px rgba($black,.075); 240 | $btn-active-box-shadow: inset 0 3px 5px rgba($black,.125); 241 | $btn-link-disabled-color: $gray-600; 242 | $btn-block-spacing-y: .5rem; 243 | $btn-border-radius: $border-radius; 244 | $btn-border-radius-lg: $border-radius-lg; 245 | $btn-border-radius-sm: $border-radius-sm; 246 | $btn-transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out; 247 | 248 | 249 | // 250 | //Forms 251 | // 252 | 253 | $input-bg: $white; 254 | $input-disabled-bg: $gray-200; 255 | $input-color: $gray-700; 256 | $input-border-color: $gray-400; 257 | $input-btn-border-width: $border-width; 258 | $input-box-shadow: inset 0 1px 1px rgba($black,.075); 259 | $input-border-radius: $border-radius; 260 | $input-border-radius-lg: $border-radius-lg; 261 | $input-border-radius-sm: $border-radius-sm; 262 | $input-focus-bg: $input-bg; 263 | $input-focus-border-color: lighten(theme-color("primary"), 25%); 264 | $input-focus-color: $input-color; 265 | $input-placeholder-color: $gray-600; 266 | $input-height-border: $input-btn-border-width * 2; 267 | $input-height-inner: ($font-size-base * $input-btn-line-height) + ($input-btn-padding-y * 2); 268 | $input-height: calc(#{$input-height-inner} + #{$input-height-border}); 269 | $input-height-inner-sm: ($font-size-sm * $input-btn-line-height-sm) + ($input-btn-padding-y-sm * 2); 270 | $input-height-sm: calc(#{$input-height-inner-sm} + #{$input-height-border}); 271 | $input-height-inner-lg: ($font-size-lg * $input-btn-line-height-lg) + ($input-btn-padding-y-lg * 2); 272 | $input-height-lg: calc(#{$input-height-inner-lg} + #{$input-height-border}); 273 | $input-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; 274 | $form-text-margin-top: .25rem; 275 | $form-check-margin-bottom: .5rem; 276 | $form-check-input-gutter: 1.25rem; 277 | $form-check-input-margin-y: .25rem; 278 | $form-check-input-margin-x: .25rem; 279 | $form-check-inline-margin-x: .75rem; 280 | $form-group-margin-bottom: 1rem; 281 | $input-group-addon-color: $input-color; 282 | $input-group-addon-bg: $gray-200; 283 | $input-group-addon-border-color: $input-border-color; 284 | $form-feedback-valid-color: theme-color("success"); 285 | $form-feedback-invalid-color: theme-color("danger"); 286 | 287 | 288 | // 289 | //CustomForms 290 | // 291 | 292 | $custom-control-gutter: 1.5rem; 293 | $custom-control-spacer-y: .25rem; 294 | $custom-control-spacer-x: 1rem; 295 | $custom-control-indicator-size: 1rem; 296 | $custom-control-indicator-bg: #ddd; 297 | $custom-control-indicator-bg-size: 50% 50%; 298 | $custom-control-indicator-box-shadow: inset 0 .25rem .25rem rgba($black,.1); 299 | $custom-control-indicator-disabled-bg: $gray-200; 300 | $custom-control-description-disabled-color: $gray-600; 301 | $custom-control-indicator-checked-color: $white; 302 | $custom-control-indicator-checked-bg: theme-color("primary"); 303 | $custom-control-indicator-checked-box-shadow: none; 304 | $custom-control-indicator-focus-box-shadow: 0 0 0 1px $body-bg, $input-btn-focus-box-shadow; 305 | $custom-control-indicator-active-color: $white; 306 | $custom-control-indicator-active-bg: lighten(theme-color("primary"), 35%); 307 | $custom-control-indicator-active-box-shadow: none; 308 | $custom-checkbox-indicator-border-radius: $border-radius; 309 | $custom-checkbox-indicator-icon-checked: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='#{$custom-control-indicator-checked-color}' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E"), "#", "%23"); 310 | $custom-checkbox-indicator-indeterminate-bg: theme-color("primary"); 311 | $custom-checkbox-indicator-indeterminate-color: $custom-control-indicator-checked-color; 312 | $custom-checkbox-indicator-icon-indeterminate: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='#{$custom-checkbox-indicator-indeterminate-color}' d='M0 2h4'/%3E%3C/svg%3E"), "#", "%23"); 313 | $custom-checkbox-indicator-indeterminate-box-shadow: none; 314 | $custom-radio-indicator-border-radius: 50%; 315 | $custom-radio-indicator-icon-checked: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='#{$custom-control-indicator-checked-color}'/%3E%3C/svg%3E"), "#", "%23"); 316 | $custom-select-padding-y: .375rem; 317 | $custom-select-padding-x: .75rem; 318 | $custom-select-height: $input-height; 319 | $custom-select-indicator-padding: 1rem; 320 | $custom-select-line-height: $input-btn-line-height; 321 | $custom-select-color: $input-color; 322 | $custom-select-disabled-color: $gray-600; 323 | $custom-select-bg: $white; 324 | $custom-select-disabled-bg: $gray-200; 325 | $custom-select-bg-size: 8px 10px; 326 | $custom-select-indicator-color: #333; 327 | $custom-select-indicator: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='#{$custom-select-indicator-color}' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E"), "#", "%23"); 328 | $custom-select-border-width: $input-btn-border-width; 329 | $custom-select-border-color: $input-border-color; 330 | $custom-select-border-radius: $border-radius; 331 | $custom-select-focus-border-color: lighten(theme-color("primary"), 25%); 332 | $custom-select-focus-box-shadow: inset 0 1px 2px rgba($black, .075), 0 0 5px rgba($custom-select-focus-border-color, .5); 333 | $custom-select-font-size-sm: 75%; 334 | $custom-select-height-sm: $input-height-sm; 335 | $custom-file-height: $input-height; 336 | $custom-file-width: 14rem; 337 | $custom-file-focus-box-shadow: 0 0 0 .075rem $white, 0 0 0 .2rem theme-color("primary"); 338 | $custom-file-padding-y: $input-btn-padding-y; 339 | $custom-file-padding-x: $input-btn-padding-x; 340 | $custom-file-line-height: $input-btn-line-height; 341 | $custom-file-color: $input-color; 342 | $custom-file-bg: $input-bg; 343 | $custom-file-border-width: $input-btn-border-width; 344 | $custom-file-border-color: $input-border-color; 345 | $custom-file-border-radius: $input-border-radius; 346 | $custom-file-box-shadow: $input-box-shadow; 347 | $custom-file-button-color: $custom-file-color; 348 | $custom-file-button-bg: $input-group-addon-bg; 349 | $custom-file-text: (placeholder: (en: "Choose file..."),button-label: (en: "Browse")); 350 | 351 | 352 | // 353 | //Dropdowns 354 | // 355 | 356 | $dropdown-min-width: 10rem; 357 | $dropdown-padding-y: .5rem; 358 | $dropdown-spacer: .125rem; 359 | $dropdown-bg: $white; 360 | $dropdown-border-color: rgba($black,.15); 361 | $dropdown-border-width: $border-width; 362 | $dropdown-divider-bg: $gray-200; 363 | $dropdown-box-shadow: 0 .5rem 1rem rgba($black,.175); 364 | $dropdown-link-color: $gray-900; 365 | $dropdown-link-hover-color: darken($gray-900, 5%); 366 | $dropdown-link-hover-bg: $gray-100; 367 | $dropdown-link-active-color: $component-active-color; 368 | $dropdown-link-active-bg: $component-active-bg; 369 | $dropdown-link-disabled-color: $gray-600; 370 | $dropdown-item-padding-y: .25rem; 371 | $dropdown-item-padding-x: 1.5rem; 372 | $dropdown-header-color: $gray-600; 373 | 374 | 375 | // 376 | //ZindexMasterList 377 | // 378 | 379 | $zindex-dropdown: 1000; 380 | $zindex-sticky: 1020; 381 | $zindex-fixed: 1030; 382 | $zindex-modal-backdrop: 1040; 383 | $zindex-modal: 1050; 384 | $zindex-popover: 1060; 385 | $zindex-tooltip: 1070; 386 | 387 | 388 | // 389 | //Navs 390 | // 391 | 392 | $nav-link-padding-y: .5rem; 393 | $nav-link-padding-x: 1rem; 394 | $nav-link-disabled-color: $gray-600; 395 | $nav-tabs-border-color: #dddddd; 396 | $nav-tabs-border-width: $border-width; 397 | $nav-tabs-border-radius: $border-radius; 398 | $nav-tabs-link-hover-border-color: $gray-200; 399 | $nav-tabs-link-active-color: $gray-700; 400 | $nav-tabs-link-active-bg: $body-bg; 401 | $nav-tabs-link-active-border-color: #dddddd; 402 | $nav-pills-border-radius: $border-radius; 403 | $nav-pills-link-active-color: $component-active-color; 404 | $nav-pills-link-active-bg: $component-active-bg; 405 | 406 | 407 | // 408 | //Navbar 409 | // 410 | 411 | $navbar-padding-y: ($spacer / 2); 412 | $navbar-padding-x: $spacer; 413 | $navbar-brand-font-size: $font-size-lg; 414 | $nav-link-height: ($font-size-base * $line-height-base + $nav-link-padding-y * 2); 415 | $navbar-brand-height: $navbar-brand-font-size * $line-height-base; 416 | $navbar-brand-padding-y: ($nav-link-height - $navbar-brand-height) / 2; 417 | $navbar-toggler-padding-y: .25rem; 418 | $navbar-toggler-padding-x: .75rem; 419 | $navbar-toggler-font-size: $font-size-lg; 420 | $navbar-toggler-border-radius: $btn-border-radius; 421 | $navbar-dark-color: rgba($white,.5); 422 | $navbar-dark-hover-color: rgba($white,.75); 423 | $navbar-dark-active-color: $white; 424 | $navbar-dark-disabled-color: rgba($white,.25); 425 | $navbar-dark-toggler-icon-bg: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{$navbar-dark-color}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"), "#", "%23"); 426 | $navbar-dark-toggler-border-color: rgba($white,.1); 427 | $navbar-light-color: rgba($black,.5); 428 | $navbar-light-hover-color: rgba($black,.7); 429 | $navbar-light-active-color: rgba($black,.9); 430 | $navbar-light-disabled-color: rgba($black,.3); 431 | $navbar-light-toggler-icon-bg: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{$navbar-light-color}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"), "#", "%23"); 432 | $navbar-light-toggler-border-color: rgba($black,.1); 433 | 434 | 435 | // 436 | //Pagination 437 | // 438 | 439 | $pagination-padding-y: .5rem; 440 | $pagination-padding-x: .75rem; 441 | $pagination-padding-y-sm: .25rem; 442 | $pagination-padding-x-sm: .5rem; 443 | $pagination-padding-y-lg: .75rem; 444 | $pagination-padding-x-lg: 1.5rem; 445 | $pagination-line-height: 1.25; 446 | $pagination-color: $link-color; 447 | $pagination-bg: $white; 448 | $pagination-border-width: $border-width; 449 | $pagination-border-color: #dddddd; 450 | $pagination-hover-color: $link-hover-color; 451 | $pagination-hover-bg: $gray-200; 452 | $pagination-hover-border-color: #dddddd; 453 | $pagination-active-color: $white; 454 | $pagination-active-bg: theme-color("primary"); 455 | $pagination-active-border-color: theme-color("primary"); 456 | $pagination-disabled-color: $gray-600; 457 | $pagination-disabled-bg: $white; 458 | $pagination-disabled-border-color: #dddddd; 459 | 460 | 461 | // 462 | //Jumbotron 463 | // 464 | 465 | $jumbotron-padding: 2rem; 466 | $jumbotron-bg: $gray-200; 467 | 468 | 469 | // 470 | //Cards 471 | // 472 | 473 | $card-spacer-y: .75rem; 474 | $card-spacer-x: 1.25rem; 475 | $card-border-width: $border-width; 476 | $card-border-radius: $border-radius; 477 | $card-border-color: rgba($black,.125); 478 | $card-inner-border-radius: calc(#{$card-border-radius} - #{$card-border-width}); 479 | $card-cap-bg: rgba($black,.03); 480 | $card-bg: $white; 481 | $card-img-overlay-padding: 1.25rem; 482 | $card-group-margin: ($grid-gutter-width / 2); 483 | $card-deck-margin: $card-group-margin; 484 | $card-columns-count: 3; 485 | $card-columns-gap: 1.25rem; 486 | $card-columns-margin: $card-spacer-y; 487 | 488 | 489 | // 490 | //Tooltips 491 | // 492 | 493 | $tooltip-max-width: 200px; 494 | $tooltip-color: $white; 495 | $tooltip-bg: $black; 496 | $tooltip-opacity: .9; 497 | $tooltip-padding-y: 3px; 498 | $tooltip-padding-x: 8px; 499 | $tooltip-margin: 0; 500 | $tooltip-arrow-width: 5px; 501 | $tooltip-arrow-height: 5px; 502 | $tooltip-arrow-color: $tooltip-bg; 503 | 504 | 505 | // 506 | //Popovers 507 | // 508 | 509 | $popover-bg: $white; 510 | $popover-max-width: 276px; 511 | $popover-border-width: $border-width; 512 | $popover-border-color: rgba($black,.2); 513 | $popover-box-shadow: 0 .25rem .5rem rgba($black,.2); 514 | $popover-header-bg: darken($popover-bg, 3%); 515 | $popover-header-color: $headings-color; 516 | $popover-header-padding-y: .5rem; 517 | $popover-header-padding-x: .75rem; 518 | $popover-body-color: $body-color; 519 | $popover-body-padding-y: $popover-header-padding-y; 520 | $popover-body-padding-x: $popover-header-padding-x; 521 | $popover-arrow-width: .8rem; 522 | $popover-arrow-height: .4rem; 523 | $popover-arrow-color: $popover-bg; 524 | $popover-arrow-outer-color: fade-in($popover-border-color, .05); 525 | 526 | 527 | // 528 | //Badges 529 | // 530 | 531 | $badge-font-size: 75%; 532 | $badge-font-weight: $font-weight-bold; 533 | $badge-padding-y: .25em; 534 | $badge-padding-x: .4em; 535 | $badge-border-radius: $border-radius; 536 | $badge-pill-padding-x: .6em; 537 | $badge-pill-border-radius: 10rem; 538 | 539 | 540 | // 541 | //Modals 542 | // 543 | 544 | $modal-inner-padding: 15px; 545 | $modal-dialog-margin: 10px; 546 | $modal-dialog-margin-y-sm-up: 30px; 547 | $modal-title-line-height: $line-height-base; 548 | $modal-content-bg: $white; 549 | $modal-content-border-color: rgba($black,.2); 550 | $modal-content-border-width: $border-width; 551 | $modal-content-box-shadow-xs: 0 3px 9px rgba($black,.5); 552 | $modal-content-box-shadow-sm-up: 0 5px 15px rgba($black,.5); 553 | $modal-backdrop-bg: $black; 554 | $modal-backdrop-opacity: .5; 555 | $modal-header-border-color: $gray-200; 556 | $modal-footer-border-color: $modal-header-border-color; 557 | $modal-header-border-width: $modal-content-border-width; 558 | $modal-footer-border-width: $modal-header-border-width; 559 | $modal-header-padding: 15px; 560 | $modal-lg: 800px; 561 | $modal-md: 500px; 562 | $modal-sm: 300px; 563 | $modal-transition: transform .3s ease-out; 564 | 565 | 566 | // 567 | //Alerts 568 | // 569 | 570 | $alert-padding-y: .75rem; 571 | $alert-padding-x: 1.5rem; 572 | $alert-margin-bottom: 1rem; 573 | $alert-border-radius: $border-radius; 574 | $alert-link-font-weight: $font-weight-bold; 575 | $alert-border-width: $border-width; 576 | 577 | 578 | // 579 | //ProgressBars 580 | // 581 | 582 | $progress-height: 1rem; 583 | $progress-font-size: ($font-size-base * .75); 584 | $progress-bg: $gray-200; 585 | $progress-border-radius: $border-radius; 586 | $progress-box-shadow: inset 0 .1rem .1rem rgba($black,.1); 587 | $progress-bar-color: $white; 588 | $progress-bar-bg: theme-color("primary"); 589 | $progress-bar-animation-timing: 1s linear infinite; 590 | $progress-bar-transition: width .6s ease; 591 | 592 | 593 | // 594 | //ListGroup 595 | // 596 | 597 | $list-group-bg: $white; 598 | $list-group-border-color: rgba($black,.125); 599 | $list-group-border-width: $border-width; 600 | $list-group-border-radius: $border-radius; 601 | $list-group-item-padding-y: .75rem; 602 | $list-group-item-padding-x: 1.25rem; 603 | $list-group-hover-bg: $gray-100; 604 | $list-group-active-color: $component-active-color; 605 | $list-group-active-bg: $component-active-bg; 606 | $list-group-active-border-color: $list-group-active-bg; 607 | $list-group-disabled-color: $gray-600; 608 | $list-group-disabled-bg: $list-group-bg; 609 | $list-group-action-color: $gray-700; 610 | $list-group-action-hover-color: $list-group-action-color; 611 | $list-group-action-active-color: $body-color; 612 | $list-group-action-active-bg: $gray-200; 613 | 614 | 615 | // 616 | //Images 617 | // 618 | 619 | $thumbnail-padding: .25rem; 620 | $thumbnail-bg: $body-bg; 621 | $thumbnail-border-width: $border-width; 622 | $thumbnail-border-color: #ddd; 623 | $thumbnail-border-radius: $border-radius; 624 | $thumbnail-box-shadow: 0 1px 2px rgba($black,.075); 625 | $thumbnail-transition: all .2s ease-in-out; 626 | 627 | 628 | // 629 | //Figures 630 | // 631 | 632 | $figure-caption-font-size: 90%; 633 | $figure-caption-color: $gray-600; 634 | 635 | 636 | // 637 | //Breadcrumbs 638 | // 639 | 640 | $breadcrumb-padding-y: .75rem; 641 | $breadcrumb-padding-x: 1rem; 642 | $breadcrumb-item-padding: .5rem; 643 | $breadcrumb-margin-bottom: 1rem; 644 | $breadcrumb-bg: $gray-200; 645 | $breadcrumb-divider-color: $gray-600; 646 | $breadcrumb-active-color: $gray-600; 647 | $breadcrumb-divider: "/"; 648 | 649 | 650 | // 651 | //Carousel 652 | // 653 | 654 | $carousel-control-color: $white; 655 | $carousel-control-width: 15%; 656 | $carousel-control-opacity: .5; 657 | $carousel-indicator-width: 30px; 658 | $carousel-indicator-height: 3px; 659 | $carousel-indicator-spacer: 3px; 660 | $carousel-indicator-active-bg: $white; 661 | $carousel-caption-width: 70%; 662 | $carousel-caption-color: $white; 663 | $carousel-control-icon-width: 20px; 664 | $carousel-control-prev-icon-bg: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='#{$carousel-control-color}' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E"), "#", "%23"); 665 | $carousel-control-next-icon-bg: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='#{$carousel-control-color}' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E"), "#", "%23"); 666 | $carousel-transition: transform .6s ease; 667 | 668 | 669 | // 670 | //Close 671 | // 672 | 673 | $close-font-size: $font-size-base * 1.5; 674 | $close-font-weight: $font-weight-bold; 675 | $close-color: $black; 676 | $close-text-shadow: 0 1px 0 $white; 677 | 678 | 679 | // 680 | //Code 681 | // 682 | 683 | $code-font-size: 90%; 684 | $code-padding-y: .2rem; 685 | $code-padding-x: .4rem; 686 | $code-color: #bd4147; 687 | $code-bg: $gray-100; 688 | $kbd-color: $white; 689 | $kbd-bg: $gray-900; 690 | $pre-color: $gray-900; 691 | $pre-scrollable-max-height: 340px; 692 | 693 | 694 | // 695 | //Extra SASS variables 696 | // 697 | 698 | $link-border-width: 0px; 699 | $link-border-style: solid; 700 | $link-border-color: transparent; 701 | $link-hover-border-width: 0px; 702 | $link-hover-border-style: solid; 703 | $link-hover-border-color: transparent; 704 | $link-font-size: inherit; 705 | $link-font-weight: inherit; 706 | $link-background-color: transparent; 707 | $link-hover-background-color: transparent; 708 | $link-footer-color: $link-color; 709 | $link-footer-decoration: $link-decoration; 710 | $link-footer-hover-color: $link-hover-color; 711 | $link-footer-hover-decoration: $link-hover-decoration; 712 | $paragraph-color: inherit; 713 | $paragraph-bold-text-weight: bolder; 714 | $paragraph-bold-text-color: inherit; 715 | $btn-text-transform: none; 716 | $btn-font-size: $font-size-base; 717 | $btn-font-size-lg: $font-size-lg; 718 | $btn-font-size-sm: $font-size-sm; 719 | $btn-background-image: none; 720 | $btn-hover-background-image: none; 721 | $btn-border-width: $border-width; 722 | $navbar-nav-link-padding-y: 0.5rem; 723 | $navbar-nav-link-padding-x: 0.5rem; 724 | $navbar-nav-link-text-transform: none; 725 | $navbar-nav-link-font-size: inherit; 726 | $navbar-nav-link-font-weight: inherit; 727 | -------------------------------------------------------------------------------- /src/main/resources/static/css/main.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: #0000FF; 3 | } 4 | 5 | h2 { 6 | color: #FF0000; 7 | } 8 | 9 | footer { 10 | margin-top: 60px; 11 | } -------------------------------------------------------------------------------- /src/main/resources/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SystangoTechnologies/Springboard/e31e3d3eea44eeda07bd8c1bfa82224f2d016139/src/main/resources/static/favicon.ico -------------------------------------------------------------------------------- /src/main/resources/static/img/apple_store.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SystangoTechnologies/Springboard/e31e3d3eea44eeda07bd8c1bfa82224f2d016139/src/main/resources/static/img/apple_store.png -------------------------------------------------------------------------------- /src/main/resources/static/img/blog-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SystangoTechnologies/Springboard/e31e3d3eea44eeda07bd8c1bfa82224f2d016139/src/main/resources/static/img/blog-1.jpg -------------------------------------------------------------------------------- /src/main/resources/static/img/blog-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SystangoTechnologies/Springboard/e31e3d3eea44eeda07bd8c1bfa82224f2d016139/src/main/resources/static/img/blog-2.jpg -------------------------------------------------------------------------------- /src/main/resources/static/img/blog-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SystangoTechnologies/Springboard/e31e3d3eea44eeda07bd8c1bfa82224f2d016139/src/main/resources/static/img/blog-3.jpg -------------------------------------------------------------------------------- /src/main/resources/static/img/favicons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SystangoTechnologies/Springboard/e31e3d3eea44eeda07bd8c1bfa82224f2d016139/src/main/resources/static/img/favicons.png -------------------------------------------------------------------------------- /src/main/resources/static/img/google-play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SystangoTechnologies/Springboard/e31e3d3eea44eeda07bd8c1bfa82224f2d016139/src/main/resources/static/img/google-play.png -------------------------------------------------------------------------------- /src/main/resources/static/img/product.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SystangoTechnologies/Springboard/e31e3d3eea44eeda07bd8c1bfa82224f2d016139/src/main/resources/static/img/product.png -------------------------------------------------------------------------------- /src/main/resources/static/img/product2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SystangoTechnologies/Springboard/e31e3d3eea44eeda07bd8c1bfa82224f2d016139/src/main/resources/static/img/product2.jpg -------------------------------------------------------------------------------- /src/main/resources/static/img/shot-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SystangoTechnologies/Springboard/e31e3d3eea44eeda07bd8c1bfa82224f2d016139/src/main/resources/static/img/shot-3.png -------------------------------------------------------------------------------- /src/main/resources/static/img/springboard-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SystangoTechnologies/Springboard/e31e3d3eea44eeda07bd8c1bfa82224f2d016139/src/main/resources/static/img/springboard-logo.png -------------------------------------------------------------------------------- /src/main/resources/static/img/team-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SystangoTechnologies/Springboard/e31e3d3eea44eeda07bd8c1bfa82224f2d016139/src/main/resources/static/img/team-1.jpg -------------------------------------------------------------------------------- /src/main/resources/static/img/team-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SystangoTechnologies/Springboard/e31e3d3eea44eeda07bd8c1bfa82224f2d016139/src/main/resources/static/img/team-2.jpg -------------------------------------------------------------------------------- /src/main/resources/static/img/team-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SystangoTechnologies/Springboard/e31e3d3eea44eeda07bd8c1bfa82224f2d016139/src/main/resources/static/img/team-3.jpg -------------------------------------------------------------------------------- /src/main/resources/static/img/team-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SystangoTechnologies/Springboard/e31e3d3eea44eeda07bd8c1bfa82224f2d016139/src/main/resources/static/img/team-4.jpg -------------------------------------------------------------------------------- /src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Springboard 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 46 | 47 | 48 |
49 |
50 |
51 |
52 | 53 |
54 |
55 |

Experience Spring Boot With Springboard Project

56 |

57 | Production ready boilerplate for Spring Boot based web applications! 58 |

59 |

60 | Get Started 62 |

63 |
64 |
65 |
66 |
67 | 68 | 69 |
70 |
71 |
72 |
73 |

Amazing Features

74 |

75 | Springboard comes with plenty of awesome features to
wow the JAVA developers. 76 |

77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | 85 |
86 |
Opinionated Architecture
87 |

88 | Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Lorem ipsum dolor sit amet. Lorem 89 | ipsum dolor sit amet, consectetuer adipiscing elit. 90 |

91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 | 99 |
100 |
JWT Support
101 |

102 | Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Lorem ipsum dolor sit amet. Lorem 103 | ipsum dolor sit amet, consectetuer adipiscing elit. 104 |

105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 | 113 |
114 |
Built-in API Versioning
115 |

116 | Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Lorem ipsum dolor sit amet. Lorem 117 | ipsum dolor sit amet, consectetuer adipiscing elit. 118 |

119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 | 127 |
128 |
JAR/WAR support
129 |

130 | Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Lorem ipsum dolor sit amet. Lorem 131 | ipsum dolor sit amet, consectetuer adipiscing elit. 132 |

133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 | 141 |
142 |
Highly Secure
143 |

144 | Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Lorem ipsum dolor sit amet. Lorem 145 | ipsum dolor sit amet, consectetuer adipiscing elit. 146 |

147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 | 155 |
156 |
Bootstrap 4 support
157 |

158 | Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Lorem ipsum dolor sit amet. Lorem 159 | ipsum dolor sit amet, consectetuer adipiscing elit. 160 |

161 |
162 |
163 |
164 |
165 |
166 |
167 | 168 |
169 |
170 |
171 |
172 | Gallery 173 |
174 |
175 |

What's New in Version 1.0?

176 |

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula 177 | eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur 178 | ridiculus mus.

179 |
    180 |
  • Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  • 181 |
  • Aenean commodo ligula eget dolor.
  • 182 |
  • Aenean massa.
  • 183 |
  • Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
  • 184 |
  • Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  • 185 |
  • Aenean commodo ligula eget dolor.
  • 186 |
  • Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
  • 187 |
188 | Get Started with Springboard 189 |
190 |
191 |
192 |
193 | 194 | 195 |
196 |
197 |
198 |
199 |

Our Team

200 |

201 | Meet our awesome team. 202 |

203 |
204 |
205 |
206 |
207 | Male 208 |
Vinita Rathi
209 |

CEO, Springboard:Systango

210 |
211 |
212 | Male 213 |
Arpit Khandelwal
214 |

CTO, Springboard:Systango

215 |
216 |
217 | Male 218 |
Brandon Lee
219 |

Lead Developer

220 |
221 |
222 | Male 223 |
Kara Thrace
224 |

Customer Care

225 |
226 |
227 |
228 |
229 | 230 | 231 |
232 |
233 |
234 |
235 |

From Our Blog

236 |

237 | The latest news from Springboard blog. 238 |

239 |
240 |
241 |
242 |
243 |
244 | Image 245 |
246 |

247 | 22h ago    248 | Arpit Khandelwal    249 | 5min read 250 |

251 |
Springboard version 1.0 is available now.
252 |

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Lorem ipsum dolor 253 | sit amet. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Lorem ipsum dolor sit 254 | amet.

255 |
256 |
257 | Read more 258 |
259 |
260 |
261 |
262 |
263 | Image 264 |
265 |

266 | 1week ago    267 | Vinita Rathi    268 | 2min read 269 |

270 |
We celebrated our success with cake.
271 |

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Lorem ipsum dolor 272 | sit amet. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Lorem ipsum dolor sit 273 | amet.

274 |
275 |
276 | Read more 277 |
278 |
279 |
280 |
281 |
282 | Image 283 |
284 |

285 | 1mon ago    286 | Arpit Khandelwal    287 | 10min read 288 |

289 |
Connecting Spring with the people.
290 |

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Lorem ipsum dolor 291 | sit amet. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Lorem ipsum dolor sit 292 | amet.

293 |
294 |
295 | Read more 296 |
297 |
298 |
299 |
300 |
301 |
302 | 303 | 304 |
305 |
306 |
307 |
308 |

309 |

+44 1253 547777

310 |
311 |
312 |

313 |

Hello@Systango.com

314 |
315 |
316 |

317 |

London, UK

318 |
319 |
320 |
321 |
322 | 323 | 324 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | -------------------------------------------------------------------------------- /src/main/resources/static/js/scripts.js: -------------------------------------------------------------------------------- 1 | /* 2 | Created on : Jul 4, 2017, 12:43:10 AM 3 | Author : Atta-Ur-Rehman Shah (http://attacomsian.com) 4 | */ 5 | $(function () { 6 | //init 7 | init(); 8 | //init wow effects 9 | new WOW().init(); 10 | 11 | //scroll menu 12 | $(window).scroll(function () { 13 | init(); 14 | }); 15 | 16 | //page scroll 17 | $('a.page-scroll').bind('click', function (event) { 18 | var $anchor = $(this); 19 | $('html, body').stop().animate({ 20 | scrollTop: $($anchor.attr('href')).offset().top - 50 21 | }, 1500, 'easeInOutExpo'); 22 | event.preventDefault(); 23 | }); 24 | 25 | //init function 26 | function init() { 27 | var secondFeature = $('#features').offset().top; 28 | var scroll = $(window).scrollTop(); 29 | if (scroll >= 150) { 30 | $('.sticky-navigation').css({"background-color": '#d81b60'}); 31 | } else { 32 | $('.sticky-navigation').css({"background-color": 'transparent'}); 33 | } 34 | if (scroll >= secondFeature - 200) { 35 | $(".mobileScreen").css({'background-position': 'center top'}); 36 | } 37 | return false; 38 | } 39 | }); -------------------------------------------------------------------------------- /src/main/resources/templates/error/403.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 |
9 | 10 |
11 | 12 |
13 |

403 - Access is denied

14 |
Hello '[[${#httpServletRequest.remoteUser}]]', 15 | you do not have permission to access this page. 16 |
17 |
18 | 19 |
20 | 21 | 22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /src/main/resources/templates/fragments/footer.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 |
8 | 9 |
10 | 11 |
12 | 13 | © 2017 mkyong.com 14 | 15 | | Logged user: | 16 | Roles: | 17 | Sign Out 18 | 19 | 21 |
22 |
23 | 24 |
25 | 26 | -------------------------------------------------------------------------------- /src/main/resources/templates/fragments/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 7 | 9 |
10 | 11 | 12 |
13 | 14 | 26 |
27 | 28 | -------------------------------------------------------------------------------- /src/main/resources/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Spring Security Example 5 |
6 | 7 | 8 | 9 |
10 | 11 |
12 | 13 |
14 |
15 |
16 |
17 |

Please Sign In

18 | 19 |
20 |
21 | Invalid username and password. 22 |
23 |
24 |
25 |
26 | You have been logged out. 27 |
28 |
29 | 30 |
31 | 33 |
34 |
35 | 37 |
38 | 39 |
40 |
41 | 42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | 51 |
52 | 53 |
54 | 55 | 56 | -------------------------------------------------------------------------------- /src/test/java/com/systango/springboard/SpringboardApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.systango.springboard; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.test.context.junit4.SpringRunner; 9 | import org.springframework.test.web.servlet.MockMvc; 10 | import org.springframework.test.web.servlet.ResultActions; 11 | import org.springframework.test.web.servlet.setup.MockMvcBuilders; 12 | import org.springframework.util.LinkedMultiValueMap; 13 | import org.springframework.util.MultiValueMap; 14 | import org.springframework.web.context.WebApplicationContext; 15 | 16 | import static org.junit.Assert.assertNotNull; 17 | import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; 18 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 19 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; 20 | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; 21 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 22 | 23 | @RunWith(SpringRunner.class) 24 | @SpringBootTest(classes = SpringboardApplication.class) 25 | public class SpringboardApplicationTests { 26 | 27 | private MockMvc mockMvc; 28 | 29 | @Autowired 30 | private WebApplicationContext webContext; 31 | 32 | @Test 33 | public void contextLoads() { 34 | } 35 | 36 | @Before 37 | public void setupMockMvc() { 38 | mockMvc = MockMvcBuilders 39 | .webAppContextSetup(webContext) 40 | .apply(springSecurity()) 41 | .build(); 42 | } 43 | 44 | @Test 45 | public void login_invalidUser() throws Exception { 46 | MultiValueMap params = new LinkedMultiValueMap<>(); 47 | params.add("username", "user"); 48 | params.add("password", "random"); 49 | 50 | ResultActions result 51 | = mockMvc.perform(post("/login") 52 | .params(params) 53 | .accept("application/json;charset=UTF-8")) 54 | .andExpect(status().is4xxClientError()); 55 | } 56 | 57 | @Test 58 | public void login_validUser() throws Exception { 59 | MultiValueMap params = new LinkedMultiValueMap<>(); 60 | params.add("username", "user"); 61 | params.add("password", "password"); 62 | 63 | ResultActions result 64 | = mockMvc.perform(post("/login") 65 | .params(params) 66 | .accept("application/json;charset=UTF-8")) 67 | .andExpect(status().isOk()); 68 | 69 | String authHeader = result.andReturn().getResponse().getHeader("Authorization"); 70 | assertNotNull(authHeader); 71 | } 72 | 73 | @Test 74 | public void login_invalidAdmin() throws Exception { 75 | MultiValueMap params = new LinkedMultiValueMap<>(); 76 | params.add("username", "admin"); 77 | params.add("password", "random"); 78 | 79 | ResultActions result 80 | = mockMvc.perform(post("/login") 81 | .params(params) 82 | .accept("application/json;charset=UTF-8")) 83 | .andExpect(status().is4xxClientError()); 84 | } 85 | 86 | @Test 87 | public void login_validAdmin() throws Exception { 88 | MultiValueMap params = new LinkedMultiValueMap<>(); 89 | params.add("username", "admin"); 90 | params.add("password", "password"); 91 | 92 | ResultActions result 93 | = mockMvc.perform(post("/login") 94 | .params(params) 95 | .accept("application/json;charset=UTF-8")) 96 | .andExpect(status().isOk()); 97 | 98 | String authHeader = result.andReturn().getResponse().getHeader("Authorization"); 99 | assertNotNull(authHeader); 100 | } 101 | 102 | @Test 103 | public void getFaqs_unauthenticatedUser() throws Exception { 104 | mockMvc.perform(get("/v1/about/faq")) 105 | .andExpect(status().is3xxRedirection()); 106 | } 107 | 108 | @Test 109 | public void getFaqs_authenticatedUser() throws Exception { 110 | String bearer = obtainAccessToken("user", "password"); 111 | assertNotNull(bearer); 112 | mockMvc.perform(get("/v1/about/faq") 113 | .header("Authorization", bearer)) 114 | .andExpect(status().is2xxSuccessful()); 115 | } 116 | 117 | @Test 118 | public void getWalletDetails_authenticatedUser() throws Exception { 119 | String bearer = obtainAccessToken("user", "password"); 120 | assertNotNull(bearer); 121 | mockMvc.perform(get("/v1/payment/wallet/{username}", "user") 122 | .header("Authorization", bearer)) 123 | .andDo(print()) 124 | .andExpect(status().is2xxSuccessful()); 125 | } 126 | 127 | /** 128 | * Method that obtains the JWT token corresponding to the passed username/pwd 129 | * 130 | * @param username 131 | * @param password 132 | * @return 133 | * @throws Exception 134 | */ 135 | private String obtainAccessToken(String username, String password) throws Exception { 136 | 137 | MultiValueMap params = new LinkedMultiValueMap<>(); 138 | params.add("username", username); 139 | params.add("password", password); 140 | 141 | ResultActions result 142 | = mockMvc.perform(post("/login") 143 | .params(params) 144 | .accept("application/json;charset=UTF-8")) 145 | .andExpect(status().isOk()); 146 | 147 | String authHeader = result.andReturn().getResponse().getHeader("Authorization"); 148 | return authHeader; 149 | } 150 | 151 | 152 | } 153 | --------------------------------------------------------------------------------