├── .gitignore
├── .mvn
└── wrapper
│ ├── maven-wrapper.jar
│ └── maven-wrapper.properties
├── README.md
├── images
├── 493fa437-762b-4c29-bb9d-d70134b18269.jpg
└── 8adbcee3-6e67-4c43-8f1b-861ecddce27f.jpg
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── lakshy
│ │ └── blog
│ │ ├── BloggingAppApisApplication.java
│ │ ├── config
│ │ ├── AppConstants.java
│ │ ├── SecurityConfig.java
│ │ └── SwaggerConfig.java
│ │ ├── controllers
│ │ ├── AuthController.java
│ │ ├── CategoryController.java
│ │ ├── CommentController.java
│ │ ├── PostController.java
│ │ └── UserController.java
│ │ ├── entities
│ │ ├── Category.java
│ │ ├── Comment.java
│ │ ├── Post.java
│ │ ├── Role.java
│ │ └── User.java
│ │ ├── exceptions
│ │ ├── ApiException.java
│ │ ├── GlobalExceptionHandler.java
│ │ ├── IncorrectFileFormatException.java
│ │ └── ResourceNotFoundException.java
│ │ ├── payloads
│ │ ├── ApiResponse.java
│ │ ├── CategoryDto.java
│ │ ├── CommentDto.java
│ │ ├── JwtAuthRequest.java
│ │ ├── JwtAuthResponse.java
│ │ ├── PostDto.java
│ │ ├── PostResponse.java
│ │ ├── RoleDto.java
│ │ └── UserDto.java
│ │ ├── repositories
│ │ ├── CategoryRepo.java
│ │ ├── CommentRepo.java
│ │ ├── PostRepo.java
│ │ ├── RoleRepo.java
│ │ └── UserRepo.java
│ │ ├── security
│ │ ├── CustomUserDetailService.java
│ │ ├── JwtAuthenticationEntryPoint.java
│ │ ├── JwtAuthenticationFilter.java
│ │ └── JwtTokenHelper.java
│ │ └── services
│ │ ├── CategoryService.java
│ │ ├── CommentService.java
│ │ ├── FileService.java
│ │ ├── PostService.java
│ │ ├── UserService.java
│ │ └── impl
│ │ ├── CategoryServiceImpl.java
│ │ ├── CommentServiceImpl.java
│ │ ├── FileServiceImpl.java
│ │ ├── PostServiceImpl.java
│ │ └── UserServiceImpl.java
└── resources
│ └── application.properties
└── test
└── java
└── com
└── lakshy
└── blog
└── BloggingAppApisApplicationTests.java
/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | !.mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**/target/
5 | !**/src/test/**/target/
6 |
7 | ### STS ###
8 | .apt_generated
9 | .classpath
10 | .factorypath
11 | .project
12 | .settings
13 | .springBeans
14 | .sts4-cache
15 |
16 | ### IntelliJ IDEA ###
17 | .idea
18 | *.iws
19 | *.iml
20 | *.ipr
21 |
22 | ### NetBeans ###
23 | /nbproject/private/
24 | /nbbuild/
25 | /dist/
26 | /nbdist/
27 | /.nb-gradle/
28 | build/
29 | !**/src/main/**/build/
30 | !**/src/test/**/build/
31 |
32 | ### VS Code ###
33 | .vscode/
34 |
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lakshygupta/Blog-App-APIs/58f1ab9309b45d63d2d7484d4657c5461b820097/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip
2 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Blog Application Backend
2 | A RESTful API for creating, reading and managing the posts/blogs. Developed using SpringBoot this REST api provides fast and secure access to anything you need. Documentation provided below.
3 |
4 | ## Built with
5 | - [Spring Boot](https://spring.io/)
6 | - [Maven](https://maven.apache.org/)
7 | - [JPA](https://spring.io/projects/spring-data-jpa)
8 | - [Hibernate](https://hibernate.org/)
9 | - [MySQL](https://www.mysql.com/)
10 |
11 | ## Overview
12 | Users should be able to create posts, Each post should have a category/topic tagged to it. Signup & login functionality of users along with password hashing. Pagination and sorting on posts. Users should be able to fetch the posts for other users or can view all the post of a particular topic. Data validation on create/update endpoints.
13 |
14 | ## Features Include
15 | - Posts CRUD
16 | - Users CRUD
17 | - Category CRUD
18 | - Comments on posts CRUD
19 | - Post limiting for pagination
20 | - Post sorting
21 | - Role based authentication
22 | - Custom Exception handling
23 | - JWT authentication
24 | - DTO pattern
25 | - Image upload
26 | - Post searching by keyword
27 | - Role specific API access
28 | - Data Validation using Hibernate validator
29 | - Documentation using Swagger
30 |
31 | ## ER Diagram
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/images/493fa437-762b-4c29-bb9d-d70134b18269.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lakshygupta/Blog-App-APIs/58f1ab9309b45d63d2d7484d4657c5461b820097/images/493fa437-762b-4c29-bb9d-d70134b18269.jpg
--------------------------------------------------------------------------------
/images/8adbcee3-6e67-4c43-8f1b-861ecddce27f.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lakshygupta/Blog-App-APIs/58f1ab9309b45d63d2d7484d4657c5461b820097/images/8adbcee3-6e67-4c43-8f1b-861ecddce27f.jpg
--------------------------------------------------------------------------------
/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 | # https://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 | # Maven 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 /usr/local/etc/mavenrc ] ; then
40 | . /usr/local/etc/mavenrc
41 | fi
42 |
43 | if [ -f /etc/mavenrc ] ; then
44 | . /etc/mavenrc
45 | fi
46 |
47 | if [ -f "$HOME/.mavenrc" ] ; then
48 | . "$HOME/.mavenrc"
49 | fi
50 |
51 | fi
52 |
53 | # OS specific support. $var _must_ be set to either true or false.
54 | cygwin=false;
55 | darwin=false;
56 | mingw=false
57 | case "`uname`" in
58 | CYGWIN*) cygwin=true ;;
59 | MINGW*) mingw=true;;
60 | Darwin*) darwin=true
61 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
62 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
63 | if [ -z "$JAVA_HOME" ]; then
64 | if [ -x "/usr/libexec/java_home" ]; then
65 | export JAVA_HOME="`/usr/libexec/java_home`"
66 | else
67 | export JAVA_HOME="/Library/Java/Home"
68 | fi
69 | fi
70 | ;;
71 | esac
72 |
73 | if [ -z "$JAVA_HOME" ] ; then
74 | if [ -r /etc/gentoo-release ] ; then
75 | JAVA_HOME=`java-config --jre-home`
76 | fi
77 | fi
78 |
79 | if [ -z "$M2_HOME" ] ; then
80 | ## resolve links - $0 may be a link to maven's home
81 | PRG="$0"
82 |
83 | # need this for relative symlinks
84 | while [ -h "$PRG" ] ; do
85 | ls=`ls -ld "$PRG"`
86 | link=`expr "$ls" : '.*-> \(.*\)$'`
87 | if expr "$link" : '/.*' > /dev/null; then
88 | PRG="$link"
89 | else
90 | PRG="`dirname "$PRG"`/$link"
91 | fi
92 | done
93 |
94 | saveddir=`pwd`
95 |
96 | M2_HOME=`dirname "$PRG"`/..
97 |
98 | # make it fully qualified
99 | M2_HOME=`cd "$M2_HOME" && pwd`
100 |
101 | cd "$saveddir"
102 | # echo Using m2 at $M2_HOME
103 | fi
104 |
105 | # For Cygwin, ensure paths are in UNIX format before anything is touched
106 | if $cygwin ; then
107 | [ -n "$M2_HOME" ] &&
108 | M2_HOME=`cygpath --unix "$M2_HOME"`
109 | [ -n "$JAVA_HOME" ] &&
110 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
111 | [ -n "$CLASSPATH" ] &&
112 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
113 | fi
114 |
115 | # For Mingw, ensure paths are in UNIX format before anything is touched
116 | if $mingw ; then
117 | [ -n "$M2_HOME" ] &&
118 | M2_HOME="`(cd "$M2_HOME"; pwd)`"
119 | [ -n "$JAVA_HOME" ] &&
120 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
121 | fi
122 |
123 | if [ -z "$JAVA_HOME" ]; then
124 | javaExecutable="`which javac`"
125 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
126 | # readlink(1) is not available as standard on Solaris 10.
127 | readLink=`which readlink`
128 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
129 | if $darwin ; then
130 | javaHome="`dirname \"$javaExecutable\"`"
131 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
132 | else
133 | javaExecutable="`readlink -f \"$javaExecutable\"`"
134 | fi
135 | javaHome="`dirname \"$javaExecutable\"`"
136 | javaHome=`expr "$javaHome" : '\(.*\)/bin'`
137 | JAVA_HOME="$javaHome"
138 | export JAVA_HOME
139 | fi
140 | fi
141 | fi
142 |
143 | if [ -z "$JAVACMD" ] ; then
144 | if [ -n "$JAVA_HOME" ] ; then
145 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
146 | # IBM's JDK on AIX uses strange locations for the executables
147 | JAVACMD="$JAVA_HOME/jre/sh/java"
148 | else
149 | JAVACMD="$JAVA_HOME/bin/java"
150 | fi
151 | else
152 | JAVACMD="`\\unset -f command; \\command -v java`"
153 | fi
154 | fi
155 |
156 | if [ ! -x "$JAVACMD" ] ; then
157 | echo "Error: JAVA_HOME is not defined correctly." >&2
158 | echo " We cannot execute $JAVACMD" >&2
159 | exit 1
160 | fi
161 |
162 | if [ -z "$JAVA_HOME" ] ; then
163 | echo "Warning: JAVA_HOME environment variable is not set."
164 | fi
165 |
166 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
167 |
168 | # traverses directory structure from process work directory to filesystem root
169 | # first directory with .mvn subdirectory is considered project base directory
170 | find_maven_basedir() {
171 |
172 | if [ -z "$1" ]
173 | then
174 | echo "Path not specified to find_maven_basedir"
175 | return 1
176 | fi
177 |
178 | basedir="$1"
179 | wdir="$1"
180 | while [ "$wdir" != '/' ] ; do
181 | if [ -d "$wdir"/.mvn ] ; then
182 | basedir=$wdir
183 | break
184 | fi
185 | # workaround for JBEAP-8937 (on Solaris 10/Sparc)
186 | if [ -d "${wdir}" ]; then
187 | wdir=`cd "$wdir/.."; pwd`
188 | fi
189 | # end of workaround
190 | done
191 | echo "${basedir}"
192 | }
193 |
194 | # concatenates all lines of a file
195 | concat_lines() {
196 | if [ -f "$1" ]; then
197 | echo "$(tr -s '\n' ' ' < "$1")"
198 | fi
199 | }
200 |
201 | BASE_DIR=`find_maven_basedir "$(pwd)"`
202 | if [ -z "$BASE_DIR" ]; then
203 | exit 1;
204 | fi
205 |
206 | ##########################################################################################
207 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
208 | # This allows using the maven wrapper in projects that prohibit checking in binary data.
209 | ##########################################################################################
210 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
211 | if [ "$MVNW_VERBOSE" = true ]; then
212 | echo "Found .mvn/wrapper/maven-wrapper.jar"
213 | fi
214 | else
215 | if [ "$MVNW_VERBOSE" = true ]; then
216 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
217 | fi
218 | if [ -n "$MVNW_REPOURL" ]; then
219 | jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
220 | else
221 | jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
222 | fi
223 | while IFS="=" read key value; do
224 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
225 | esac
226 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
227 | if [ "$MVNW_VERBOSE" = true ]; then
228 | echo "Downloading from: $jarUrl"
229 | fi
230 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
231 | if $cygwin; then
232 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
233 | fi
234 |
235 | if command -v wget > /dev/null; then
236 | if [ "$MVNW_VERBOSE" = true ]; then
237 | echo "Found wget ... using wget"
238 | fi
239 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
240 | wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
241 | else
242 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
243 | fi
244 | elif command -v curl > /dev/null; then
245 | if [ "$MVNW_VERBOSE" = true ]; then
246 | echo "Found curl ... using curl"
247 | fi
248 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
249 | curl -o "$wrapperJarPath" "$jarUrl" -f
250 | else
251 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
252 | fi
253 |
254 | else
255 | if [ "$MVNW_VERBOSE" = true ]; then
256 | echo "Falling back to using Java to download"
257 | fi
258 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
259 | # For Cygwin, switch paths to Windows format before running javac
260 | if $cygwin; then
261 | javaClass=`cygpath --path --windows "$javaClass"`
262 | fi
263 | if [ -e "$javaClass" ]; then
264 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
265 | if [ "$MVNW_VERBOSE" = true ]; then
266 | echo " - Compiling MavenWrapperDownloader.java ..."
267 | fi
268 | # Compiling the Java class
269 | ("$JAVA_HOME/bin/javac" "$javaClass")
270 | fi
271 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
272 | # Running the downloader
273 | if [ "$MVNW_VERBOSE" = true ]; then
274 | echo " - Running MavenWrapperDownloader.java ..."
275 | fi
276 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
277 | fi
278 | fi
279 | fi
280 | fi
281 | ##########################################################################################
282 | # End of extension
283 | ##########################################################################################
284 |
285 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
286 | if [ "$MVNW_VERBOSE" = true ]; then
287 | echo $MAVEN_PROJECTBASEDIR
288 | fi
289 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
290 |
291 | # For Cygwin, switch paths to Windows format before running java
292 | if $cygwin; then
293 | [ -n "$M2_HOME" ] &&
294 | M2_HOME=`cygpath --path --windows "$M2_HOME"`
295 | [ -n "$JAVA_HOME" ] &&
296 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
297 | [ -n "$CLASSPATH" ] &&
298 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
299 | [ -n "$MAVEN_PROJECTBASEDIR" ] &&
300 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
301 | fi
302 |
303 | # Provide a "standardized" way to retrieve the CLI args that will
304 | # work with both Windows and non-Windows executions.
305 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
306 | export MAVEN_CMD_LINE_ARGS
307 |
308 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
309 |
310 | exec "$JAVACMD" \
311 | $MAVEN_OPTS \
312 | $MAVEN_DEBUG_OPTS \
313 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
314 | "-Dmaven.home=${M2_HOME}" \
315 | "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
316 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
317 |
--------------------------------------------------------------------------------
/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 https://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 Maven 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 keystroke 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 set title of command window
39 | title %0
40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
42 |
43 | @REM set %HOME% to equivalent of $HOME
44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
45 |
46 | @REM Execute a user defined script before this one
47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending
49 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
50 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
51 | :skipRcPre
52 |
53 | @setlocal
54 |
55 | set ERROR_CODE=0
56 |
57 | @REM To isolate internal variables from possible post scripts, we use another setlocal
58 | @setlocal
59 |
60 | @REM ==== START VALIDATION ====
61 | if not "%JAVA_HOME%" == "" goto OkJHome
62 |
63 | echo.
64 | echo Error: JAVA_HOME not found in your environment. >&2
65 | echo Please set the JAVA_HOME variable in your environment to match the >&2
66 | echo location of your Java installation. >&2
67 | echo.
68 | goto error
69 |
70 | :OkJHome
71 | if exist "%JAVA_HOME%\bin\java.exe" goto init
72 |
73 | echo.
74 | echo Error: JAVA_HOME is set to an invalid directory. >&2
75 | echo JAVA_HOME = "%JAVA_HOME%" >&2
76 | echo Please set the JAVA_HOME variable in your environment to match the >&2
77 | echo location of your Java installation. >&2
78 | echo.
79 | goto error
80 |
81 | @REM ==== END VALIDATION ====
82 |
83 | :init
84 |
85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
86 | @REM Fallback to current working directory if not found.
87 |
88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
90 |
91 | set EXEC_DIR=%CD%
92 | set WDIR=%EXEC_DIR%
93 | :findBaseDir
94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound
95 | cd ..
96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound
97 | set WDIR=%CD%
98 | goto findBaseDir
99 |
100 | :baseDirFound
101 | set MAVEN_PROJECTBASEDIR=%WDIR%
102 | cd "%EXEC_DIR%"
103 | goto endDetectBaseDir
104 |
105 | :baseDirNotFound
106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
107 | cd "%EXEC_DIR%"
108 |
109 | :endDetectBaseDir
110 |
111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
112 |
113 | @setlocal EnableExtensions EnableDelayedExpansion
114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
116 |
117 | :endReadAdditionalConfig
118 |
119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
122 |
123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
124 |
125 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
127 | )
128 |
129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data.
131 | if exist %WRAPPER_JAR% (
132 | if "%MVNW_VERBOSE%" == "true" (
133 | echo Found %WRAPPER_JAR%
134 | )
135 | ) else (
136 | if not "%MVNW_REPOURL%" == "" (
137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
138 | )
139 | if "%MVNW_VERBOSE%" == "true" (
140 | echo Couldn't find %WRAPPER_JAR%, downloading it ...
141 | echo Downloading from: %DOWNLOAD_URL%
142 | )
143 |
144 | powershell -Command "&{"^
145 | "$webclient = new-object System.Net.WebClient;"^
146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
148 | "}"^
149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
150 | "}"
151 | if "%MVNW_VERBOSE%" == "true" (
152 | echo Finished downloading %WRAPPER_JAR%
153 | )
154 | )
155 | @REM End of extension
156 |
157 | @REM Provide a "standardized" way to retrieve the CLI args that will
158 | @REM work with both Windows and non-Windows executions.
159 | set MAVEN_CMD_LINE_ARGS=%*
160 |
161 | %MAVEN_JAVA_EXE% ^
162 | %JVM_CONFIG_MAVEN_PROPS% ^
163 | %MAVEN_OPTS% ^
164 | %MAVEN_DEBUG_OPTS% ^
165 | -classpath %WRAPPER_JAR% ^
166 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
167 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
168 | if ERRORLEVEL 1 goto error
169 | goto end
170 |
171 | :error
172 | set ERROR_CODE=1
173 |
174 | :end
175 | @endlocal & set ERROR_CODE=%ERROR_CODE%
176 |
177 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
178 | @REM check for post script, once with legacy .bat ending and once with .cmd ending
179 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
180 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
181 | :skipRcPost
182 |
183 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
184 | if "%MAVEN_BATCH_PAUSE%"=="on" pause
185 |
186 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
187 |
188 | cmd /C exit /B %ERROR_CODE%
189 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 2.7.2
9 |
10 |
11 | com.lakshy.blog
12 | blogging-app-apis
13 | 0.0.1-SNAPSHOT
14 | blogging-app-apis
15 | Backend for Blog app using Spring Boot
16 |
17 | 11
18 |
19 |
20 |
21 | org.springframework.boot
22 | spring-boot-starter-data-jpa
23 |
24 |
25 | org.springframework.boot
26 | spring-boot-starter-web
27 |
28 |
29 | org.springframework.boot
30 | spring-boot-starter-security
31 |
32 |
33 | org.springframework.boot
34 | spring-boot-starter-validation
35 |
36 |
37 | io.springfox
38 | springfox-boot-starter
39 | 3.0.0
40 |
41 |
42 | org.springframework.boot
43 | spring-boot-devtools
44 | runtime
45 | true
46 |
47 |
48 | mysql
49 | mysql-connector-java
50 | runtime
51 |
52 |
53 | io.jsonwebtoken
54 | jjwt
55 | 0.9.1
56 |
57 |
58 | org.projectlombok
59 | lombok
60 | true
61 |
62 |
63 | org.modelmapper
64 | modelmapper
65 | 3.1.0
66 |
67 |
68 | org.springframework.boot
69 | spring-boot-starter-test
70 | test
71 |
72 |
73 |
74 |
75 |
76 |
77 | org.springframework.boot
78 | spring-boot-maven-plugin
79 |
80 |
81 |
82 | org.projectlombok
83 | lombok
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/src/main/java/com/lakshy/blog/BloggingAppApisApplication.java:
--------------------------------------------------------------------------------
1 | package com.lakshy.blog;
2 |
3 | import java.util.List;
4 |
5 | import org.modelmapper.ModelMapper;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.boot.CommandLineRunner;
8 | import org.springframework.boot.SpringApplication;
9 | import org.springframework.boot.autoconfigure.SpringBootApplication;
10 | import org.springframework.context.annotation.Bean;
11 | import org.springframework.security.crypto.password.PasswordEncoder;
12 |
13 | import com.lakshy.blog.config.AppConstants;
14 | import com.lakshy.blog.entities.Role;
15 | import com.lakshy.blog.repositories.RoleRepo;
16 |
17 | @SpringBootApplication
18 | public class BloggingAppApisApplication implements CommandLineRunner {
19 |
20 | @Autowired
21 | private PasswordEncoder passwordEncoder;
22 |
23 | @Autowired
24 | private RoleRepo roleRepo;
25 |
26 | public static void main(String[] args) {
27 | SpringApplication.run(BloggingAppApisApplication.class, args);
28 | }
29 |
30 | @Bean
31 | public ModelMapper modelMapper()
32 | {
33 | return new ModelMapper();
34 | }
35 |
36 | // will run automatically when main runs and passing arguments via terminal
37 | @Override
38 | public void run(String... args) throws Exception {
39 |
40 | // System.out.println(this.passwordEncoder.encode("12345"));
41 |
42 | // Creating 2 roles at the start of application
43 | try {
44 |
45 | Role role = new Role();
46 | role.setId(AppConstants.ADMIN_USER);
47 | role.setName("ROLE_ADMIN");
48 |
49 | Role role1 = new Role();
50 | role1.setId(AppConstants.NORMAL_USER);
51 | role1.setName("ROLE_NORMAL");
52 |
53 | List roles = List.of(role,role1);
54 |
55 | List result = this.roleRepo.saveAll(roles);
56 |
57 | result.forEach(r -> {
58 | System.out.println(r.getName());
59 | });
60 |
61 | } catch (Exception e) {
62 | e.printStackTrace();
63 | }
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/com/lakshy/blog/config/AppConstants.java:
--------------------------------------------------------------------------------
1 | package com.lakshy.blog.config;
2 |
3 | // All constants and hard coded values
4 | public class AppConstants {
5 | public static final String PAGE_NUMBER = "0";
6 | public static final String PAGE_SIZE = "10";
7 | public static final String SOTRT_BY = "postId";
8 | public static final String SORT_DIR = "asc";
9 | public static final Integer NORMAL_USER = 502;
10 | public static final Integer ADMIN_USER = 501;
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/lakshy/blog/config/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | package com.lakshy.blog.config;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.context.annotation.Bean;
5 | import org.springframework.context.annotation.Configuration;
6 | import org.springframework.http.HttpMethod;
7 | import org.springframework.security.authentication.AuthenticationManager;
8 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
9 | import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
10 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
11 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
12 | import org.springframework.security.web.SecurityFilterChain;
13 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
14 | import org.springframework.web.servlet.config.annotation.EnableWebMvc;
15 |
16 | import com.lakshy.blog.security.CustomUserDetailService;
17 | import com.lakshy.blog.security.JwtAuthenticationEntryPoint;
18 | import com.lakshy.blog.security.JwtAuthenticationFilter;
19 |
20 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
21 | import org.springframework.security.config.http.SessionCreationPolicy;
22 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
23 | import org.springframework.security.crypto.password.PasswordEncoder;
24 |
25 | @Configuration
26 | @EnableWebSecurity
27 | @EnableWebMvc
28 | @EnableGlobalMethodSecurity(prePostEnabled = true)
29 | public class SecurityConfig {
30 |
31 | public static final String[] PUBLIC_URLS = {
32 | "/api/v1/auth/**",
33 | "/v3/api-docs",
34 | "/v3/api-docs/**",
35 | "/v2/api-docs",
36 | "/swagger-resources/**",
37 | "/swagger-ui/**",
38 | "/webjars/**"
39 | };
40 |
41 | @Autowired
42 | private CustomUserDetailService customUserDetailService;
43 |
44 | @Autowired
45 | private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
46 |
47 | @Autowired
48 | private JwtAuthenticationFilter jwtAuthenticationFilter;
49 |
50 |
51 | @Bean
52 | public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
53 | http
54 | .csrf()
55 | .disable()
56 | .authorizeHttpRequests()
57 | .antMatchers(PUBLIC_URLS).permitAll()
58 | .antMatchers(HttpMethod.GET).permitAll()
59 | .anyRequest()
60 | .authenticated()
61 | .and()
62 | .exceptionHandling()
63 | .authenticationEntryPoint(this.jwtAuthenticationEntryPoint)
64 | .and()
65 | .sessionManagement()
66 | .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
67 |
68 | http
69 | .addFilterBefore(this.jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
70 | return http.build();
71 | }
72 |
73 |
74 | /*
75 | * === No Need for authenticationManager in Spring Security >= 5.7.2 ===
76 | * https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter
77 | * It is also no longer necessary to manually set UserDetailsService implementation in AuthenticationManager instance,
78 | * it only needs to exist in the spring context (example is done with the help of annotations to create beans).
79 | */
80 | // @Bean
81 | // AuthenticationManager authenticationManager(AuthenticationManagerBuilder builder) throws Exception {
82 | // return builder.userDetailsService(this.customUserDetailService).passwordEncoder(passwordEncoder()).and().build();
83 | // }
84 |
85 | @Bean
86 | public PasswordEncoder passwordEncoder() {
87 | return new BCryptPasswordEncoder();
88 | }
89 |
90 | @Bean
91 | public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
92 | return authenticationConfiguration.getAuthenticationManager();
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/main/java/com/lakshy/blog/config/SwaggerConfig.java:
--------------------------------------------------------------------------------
1 | package com.lakshy.blog.config;
2 |
3 | import java.util.Collection;
4 | import java.util.Collections;
5 |
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 | import org.springframework.web.servlet.config.annotation.EnableWebMvc;
9 |
10 | import springfox.documentation.builders.PathSelectors;
11 | import springfox.documentation.builders.RequestHandlerSelectors;
12 | import springfox.documentation.service.ApiInfo;
13 | import springfox.documentation.service.Contact;
14 | import springfox.documentation.spi.DocumentationType;
15 | import springfox.documentation.spring.web.plugins.Docket;
16 |
17 | @Configuration
18 | @EnableWebMvc
19 | public class SwaggerConfig {
20 | @Bean
21 | public Docket api() {
22 | return new Docket(DocumentationType.SWAGGER_2).apiInfo(getInfo())
23 | .select().apis(RequestHandlerSelectors.any()).paths(PathSelectors.any()).build();
24 | }
25 |
26 | private ApiInfo getInfo() {
27 |
28 | return new ApiInfo("Blog App APIs", "Backend API for Blogging App using Java SpringBoot", "1.0", "Terms of Service", new Contact("Lakshy Gupta", "https://github.com/lakshygupta", "lakshygupta99@gmail.com"), "License of APIs", "API License URL", Collections.emptyList());
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/com/lakshy/blog/controllers/AuthController.java:
--------------------------------------------------------------------------------
1 | package com.lakshy.blog.controllers;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.http.HttpStatus;
5 | import org.springframework.http.ResponseEntity;
6 | import org.springframework.security.authentication.AuthenticationManager;
7 | import org.springframework.security.authentication.BadCredentialsException;
8 | import org.springframework.security.authentication.DisabledException;
9 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
10 | import org.springframework.security.core.userdetails.UserDetails;
11 | import org.springframework.security.core.userdetails.UserDetailsService;
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 com.lakshy.blog.exceptions.ApiException;
18 | import com.lakshy.blog.payloads.JwtAuthRequest;
19 | import com.lakshy.blog.payloads.JwtAuthResponse;
20 | import com.lakshy.blog.payloads.UserDto;
21 | import com.lakshy.blog.security.JwtTokenHelper;
22 | import com.lakshy.blog.services.UserService;
23 |
24 | @RestController
25 | @RequestMapping("/api/v1/auth")
26 | public class AuthController {
27 |
28 | @Autowired
29 | private JwtTokenHelper jwtTokenHelper;
30 |
31 | @Autowired
32 | private UserDetailsService userDetailService;
33 |
34 | @Autowired
35 | private AuthenticationManager authenticationManager;
36 |
37 | @Autowired
38 | private UserService userService;
39 |
40 | @PostMapping("/login")
41 | public ResponseEntity createToken(
42 | @RequestBody JwtAuthRequest request
43 | ) throws Exception{
44 |
45 | this.authenticate(request.getUsername(),request.getPassword());
46 | UserDetails userDetails = this.userDetailService.loadUserByUsername(request.getUsername());
47 |
48 | String token = this.jwtTokenHelper.generateToken(userDetails);
49 |
50 | JwtAuthResponse response = new JwtAuthResponse();
51 | response.setToken(token);
52 |
53 | return new ResponseEntity(response,HttpStatus.OK);
54 | }
55 |
56 | private void authenticate(String username, String password) throws Exception {
57 | UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(username, password);
58 | // we might get exceptions here i.e. user disabled handling it in global exception
59 | try {
60 | this.authenticationManager.authenticate(usernamePasswordAuthenticationToken);
61 | }
62 | catch(BadCredentialsException ex) {
63 | System.out.println("invalid details of user in request");
64 | throw new ApiException("Invalid Username or password");
65 | }
66 | }
67 |
68 | // register new user
69 | @PostMapping("/register")
70 | public ResponseEntity registerNewUser(@RequestBody UserDto userDto){
71 | UserDto newUser = this.userService.registerNewUser(userDto);
72 |
73 | return new ResponseEntity(newUser,HttpStatus.OK);
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/com/lakshy/blog/controllers/CategoryController.java:
--------------------------------------------------------------------------------
1 | package com.lakshy.blog.controllers;
2 |
3 | import java.util.List;
4 |
5 | import javax.validation.Valid;
6 |
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.http.HttpStatus;
9 | import org.springframework.http.ResponseEntity;
10 | import org.springframework.security.access.prepost.PreAuthorize;
11 | import org.springframework.web.bind.annotation.DeleteMapping;
12 | import org.springframework.web.bind.annotation.GetMapping;
13 | import org.springframework.web.bind.annotation.PathVariable;
14 | import org.springframework.web.bind.annotation.PostMapping;
15 | import org.springframework.web.bind.annotation.PutMapping;
16 | import org.springframework.web.bind.annotation.RequestBody;
17 | import org.springframework.web.bind.annotation.RequestMapping;
18 | import org.springframework.web.bind.annotation.RestController;
19 |
20 | import com.lakshy.blog.payloads.ApiResponse;
21 | import com.lakshy.blog.payloads.CategoryDto;
22 | import com.lakshy.blog.services.CategoryService;
23 |
24 | @RestController
25 | @RequestMapping("/api/categories")
26 | public class CategoryController {
27 |
28 | @Autowired
29 | private CategoryService categoryService;
30 |
31 | @PostMapping("/")
32 | public ResponseEntity createCategory(@Valid @RequestBody CategoryDto categoryDto)
33 | {
34 | CategoryDto savedCategory = categoryService.createCategory(categoryDto);
35 | return new ResponseEntity(savedCategory, HttpStatus.CREATED);
36 | }
37 |
38 | @PutMapping("/{categoryId}")
39 | public ResponseEntity updateCategory(@Valid @RequestBody CategoryDto categoryDto, @PathVariable Integer categoryId)
40 | {
41 | CategoryDto updatedCategory = categoryService.updateCategory(categoryDto, categoryId);
42 | return new ResponseEntity(updatedCategory, HttpStatus.OK);
43 | }
44 |
45 | // Admin only
46 | @PreAuthorize("hasRole('ADMIN')")
47 | @DeleteMapping("/{categoryId}")
48 | public ResponseEntity deleteCategory(@PathVariable Integer categoryId)
49 | {
50 | categoryService.deleteCategory(categoryId);
51 | return new ResponseEntity(new ApiResponse("Category deleted Successfully",true),HttpStatus.OK);
52 | }
53 |
54 | @GetMapping("/{categoryId}")
55 | public ResponseEntity getCategoryById(@PathVariable Integer categoryId)
56 | {
57 | CategoryDto categoryDto = categoryService.getCategoryById(categoryId);
58 | return new ResponseEntity(categoryDto,HttpStatus.OK);
59 | }
60 |
61 | @GetMapping("/")
62 | public ResponseEntity> getAllCategories()
63 | {
64 | List categories = categoryService.getAllCategories();
65 | return new ResponseEntity>(categories,HttpStatus.OK);
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/com/lakshy/blog/controllers/CommentController.java:
--------------------------------------------------------------------------------
1 | package com.lakshy.blog.controllers;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.http.HttpStatus;
5 | import org.springframework.http.ResponseEntity;
6 | import org.springframework.security.access.prepost.PreAuthorize;
7 | import org.springframework.web.bind.annotation.DeleteMapping;
8 | import org.springframework.web.bind.annotation.PathVariable;
9 | import org.springframework.web.bind.annotation.PostMapping;
10 | import org.springframework.web.bind.annotation.RequestBody;
11 | import org.springframework.web.bind.annotation.RequestMapping;
12 | import org.springframework.web.bind.annotation.RestController;
13 |
14 | import com.lakshy.blog.payloads.ApiResponse;
15 | import com.lakshy.blog.payloads.CommentDto;
16 | import com.lakshy.blog.services.CommentService;
17 |
18 | @RestController
19 | @RequestMapping("/api")
20 | public class CommentController {
21 |
22 | @Autowired
23 | private CommentService commentService;
24 |
25 | @PostMapping("/post/{postId}/user/{userId}/comments")
26 | public ResponseEntity createComment(@RequestBody CommentDto comment,@PathVariable Integer postId,@PathVariable Integer userId)
27 | {
28 | CommentDto createComment = this.commentService.createComment(comment, postId, userId);
29 | return new ResponseEntity(createComment,HttpStatus.CREATED);
30 | }
31 |
32 | // Admin only
33 | @PreAuthorize("hasRole('ADMIN')")
34 | @DeleteMapping("/comments/{commentId}")
35 | public ResponseEntity deleteComment(@PathVariable Integer commentId)
36 | {
37 | this.commentService.deleteComment(commentId);
38 | return new ResponseEntity(new ApiResponse("Comment deleted successfully",true),HttpStatus.OK);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/com/lakshy/blog/controllers/PostController.java:
--------------------------------------------------------------------------------
1 | package com.lakshy.blog.controllers;
2 |
3 | import java.io.FileNotFoundException;
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 | import java.util.List;
7 |
8 | import javax.servlet.http.HttpServletResponse;
9 |
10 | import org.hibernate.engine.jdbc.StreamUtils;
11 | import org.springframework.beans.factory.annotation.Autowired;
12 | import org.springframework.beans.factory.annotation.Value;
13 | import org.springframework.http.HttpStatus;
14 | import org.springframework.http.MediaType;
15 | import org.springframework.http.ResponseEntity;
16 | import org.springframework.security.access.prepost.PreAuthorize;
17 | import org.springframework.web.bind.annotation.DeleteMapping;
18 | import org.springframework.web.bind.annotation.GetMapping;
19 | import org.springframework.web.bind.annotation.PathVariable;
20 | import org.springframework.web.bind.annotation.PostMapping;
21 | import org.springframework.web.bind.annotation.PutMapping;
22 | import org.springframework.web.bind.annotation.RequestBody;
23 | import org.springframework.web.bind.annotation.RequestMapping;
24 | import org.springframework.web.bind.annotation.RequestParam;
25 | import org.springframework.web.bind.annotation.RestController;
26 | import org.springframework.web.multipart.MultipartFile;
27 |
28 | import com.lakshy.blog.config.AppConstants;
29 | import com.lakshy.blog.payloads.ApiResponse;
30 | import com.lakshy.blog.payloads.PostDto;
31 | import com.lakshy.blog.payloads.PostResponse;
32 | import com.lakshy.blog.services.FileService;
33 | import com.lakshy.blog.services.PostService;
34 |
35 | @RestController
36 | @RequestMapping("/api")
37 | public class PostController {
38 |
39 | @Autowired
40 | private PostService postService;
41 |
42 | @Autowired
43 | private FileService fileService;
44 |
45 | //path from application.properties to upload images
46 | @Value("${project.image}")
47 | private String PATH;
48 |
49 | @PostMapping("/user/{userId}/category/{categoryId}/posts")
50 | public ResponseEntity createPost(@RequestBody PostDto postDto, @PathVariable Integer userId, @PathVariable Integer categoryId)
51 | {
52 | PostDto savedPost = this.postService.createPost(postDto, userId, categoryId);
53 | return new ResponseEntity(savedPost,HttpStatus.CREATED);
54 | }
55 |
56 | // get posts by category
57 | @GetMapping("/category/{categoryId}/posts")
58 | public ResponseEntity getPostsByCategory(
59 | @PathVariable Integer categoryId,
60 | @RequestParam(value="pageNumber",defaultValue = AppConstants.PAGE_NUMBER,required = false) Integer pageNumber,
61 | @RequestParam(value="pageSize",defaultValue = AppConstants.PAGE_SIZE,required = false) Integer pageSize
62 | )
63 | {
64 | PostResponse postResponse = this.postService.getPostsByCategory(categoryId,pageNumber,pageSize);
65 | return new ResponseEntity(postResponse,HttpStatus.OK);
66 | }
67 |
68 | // get posts by user
69 | @GetMapping("/user/{userId}/posts")
70 | public ResponseEntity> getPostsByUser(@PathVariable Integer userId)
71 | {
72 | List posts = this.postService.getPostsByUsers(userId);
73 | return new ResponseEntity>(posts,HttpStatus.OK);
74 | }
75 |
76 | @GetMapping("/posts/{postId}")
77 | public ResponseEntity getPostById(@PathVariable Integer postId)
78 | {
79 | PostDto post = this.postService.getPostById(postId);
80 | return new ResponseEntity(post,HttpStatus.OK);
81 | }
82 |
83 | @GetMapping("/posts")
84 | public ResponseEntity> getAllPosts(){
85 | List posts = this.postService.getAllPosts();
86 | return new ResponseEntity>(posts,HttpStatus.OK);
87 | }
88 |
89 | // Admin only
90 | @PreAuthorize("hasRole('ADMIN')")
91 | @DeleteMapping("/posts/{postId}")
92 | public ResponseEntity deletePost(@PathVariable Integer postId)
93 | {
94 | postService.deletePost(postId);
95 | return new ResponseEntity(new ApiResponse("Post Deleted successfully",true),HttpStatus.OK);
96 | }
97 |
98 | @PutMapping("/posts/{postId}")
99 | public ResponseEntity updatePost(@RequestBody PostDto postDto,@PathVariable Integer postId)
100 | {
101 | System.out.println("here");
102 | PostDto post = postService.updatePost(postDto, postId);
103 | return new ResponseEntity(post,HttpStatus.OK);
104 | }
105 |
106 | // pagination and sorting
107 | @GetMapping("/postsPage")
108 | public ResponseEntity getAllPostsByPage(
109 | @RequestParam(value="pageNumber",defaultValue = AppConstants.PAGE_NUMBER,required = false) Integer pageNumber,
110 | @RequestParam(value="pageSize",defaultValue = AppConstants.PAGE_SIZE,required = false) Integer pageSize,
111 | @RequestParam(value="sortBy", defaultValue = AppConstants.SOTRT_BY, required = false) String sortBy,
112 | @RequestParam(value="sortDir", defaultValue = AppConstants.SORT_DIR, required = false) String sortDir
113 | ){
114 | PostResponse postResponse = this.postService.getAllPostsByPage(pageNumber,pageSize,sortBy,sortDir);
115 | return new ResponseEntity(postResponse,HttpStatus.OK);
116 | }
117 |
118 | // searching
119 | @GetMapping("/posts/search/{keywords}")
120 | public ResponseEntity> searchPostByTitle(@PathVariable("keywords") String keywords)
121 | {
122 | List result = this.postService.searchPost(keywords);
123 | return new ResponseEntity>(result,HttpStatus.OK);
124 | }
125 |
126 | //post image upload
127 | @PostMapping("/post/image/upload/{postId}")
128 | public ResponseEntity uploadPostImage(
129 | @RequestParam("image") MultipartFile image,
130 | @PathVariable Integer postId
131 | ) throws IOException
132 | {
133 | PostDto postDto = this.postService.getPostById(postId);
134 | String fileName = this.fileService.uploadImage(PATH, image);
135 | postDto.setImageName(fileName);
136 | PostDto updatePost = this.postService.updatePost(postDto, postId);
137 | return new ResponseEntity(updatePost,HttpStatus.OK);
138 | }
139 |
140 | // serve image using restapi
141 | @GetMapping(value ="post/image/{imageName}",produces = MediaType.IMAGE_JPEG_VALUE)
142 | public void serveImage(
143 | @PathVariable String imageName,
144 | HttpServletResponse response
145 | ) throws IOException
146 | {
147 | InputStream resource = this.fileService.getResources(PATH, imageName);
148 | response.setContentType(MediaType.IMAGE_JPEG_VALUE);
149 | StreamUtils.copy(resource, response.getOutputStream());
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/src/main/java/com/lakshy/blog/controllers/UserController.java:
--------------------------------------------------------------------------------
1 | package com.lakshy.blog.controllers;
2 |
3 | import java.util.List;
4 |
5 | import javax.validation.Valid;
6 |
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.http.HttpStatus;
9 | import org.springframework.http.ResponseEntity;
10 | import org.springframework.security.access.prepost.PreAuthorize;
11 | import org.springframework.web.bind.annotation.DeleteMapping;
12 | import org.springframework.web.bind.annotation.GetMapping;
13 | import org.springframework.web.bind.annotation.PathVariable;
14 | import org.springframework.web.bind.annotation.PostMapping;
15 | import org.springframework.web.bind.annotation.PutMapping;
16 | import org.springframework.web.bind.annotation.RequestBody;
17 | import org.springframework.web.bind.annotation.RequestMapping;
18 | import org.springframework.web.bind.annotation.RestController;
19 |
20 | import com.lakshy.blog.payloads.ApiResponse;
21 | import com.lakshy.blog.payloads.UserDto;
22 | import com.lakshy.blog.services.UserService;
23 |
24 | @RestController
25 | @RequestMapping("/api/users")
26 | public class UserController
27 | {
28 | @Autowired
29 | private UserService userService;
30 |
31 | @PostMapping("/")
32 | public ResponseEntity createUser(@Valid @RequestBody UserDto userDto)
33 | {
34 | UserDto createdUserDto = userService.createUser(userDto);
35 | return new ResponseEntity<>(createdUserDto,HttpStatus.CREATED);
36 | }
37 |
38 | @PutMapping("/{userId}")
39 | public ResponseEntity updateUser(@Valid @RequestBody UserDto userDto, @PathVariable("userId") Integer userId)
40 | {
41 | UserDto updatedUserDto = userService.updateUser(userDto, userId);
42 | return ResponseEntity.ok(updatedUserDto);
43 | }
44 |
45 | // Admin only
46 | @PreAuthorize("hasRole('ADMIN')")
47 | @DeleteMapping("/{userId}")
48 | public ResponseEntity deleteUser(@PathVariable Integer userId) // instead of ApiResponse we can also use ? as sometime we do not know the type
49 | {
50 | userService.deleteUser(userId);
51 | // return new ResponseEntity(Map.of("message","user Deleted successfully"),HttpStatus.OK);
52 | return new ResponseEntity(new ApiResponse("User Deleted Successfully",true),HttpStatus.OK);
53 | }
54 |
55 | @GetMapping("/{userId}")
56 | public ResponseEntity getUserById(@PathVariable Integer userId)
57 | {
58 | UserDto fetchedUser = userService.getUserById(userId);
59 | return new ResponseEntity(fetchedUser, HttpStatus.OK);
60 | }
61 |
62 | @GetMapping("/")
63 | public ResponseEntity> getALLUsers()
64 | {
65 | List users = userService.getAllUsers();
66 | return new ResponseEntity>(users, HttpStatus.OK);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/com/lakshy/blog/entities/Category.java:
--------------------------------------------------------------------------------
1 | package com.lakshy.blog.entities;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import javax.persistence.CascadeType;
7 | import javax.persistence.Column;
8 | import javax.persistence.Entity;
9 | import javax.persistence.FetchType;
10 | import javax.persistence.GeneratedValue;
11 | import javax.persistence.GenerationType;
12 | import javax.persistence.Id;
13 | import javax.persistence.OneToMany;
14 | import javax.persistence.Table;
15 |
16 | import lombok.Getter;
17 | import lombok.NoArgsConstructor;
18 | import lombok.Setter;
19 |
20 |
21 | @Entity
22 | @Table(name = "categories")
23 | @Getter
24 | @Setter
25 | @NoArgsConstructor
26 | public class Category {
27 | @Id
28 | @GeneratedValue(strategy = GenerationType.IDENTITY)
29 | private Integer categoryId;
30 |
31 | @Column(name="title",length=100,nullable=false)
32 | private String categoryTitle;
33 |
34 | @Column(name="description")
35 | private String categoryDescription;
36 |
37 | @OneToMany(mappedBy = "category", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
38 | private List posts = new ArrayList<>();
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/com/lakshy/blog/entities/Comment.java:
--------------------------------------------------------------------------------
1 | package com.lakshy.blog.entities;
2 |
3 | import javax.persistence.Entity;
4 | import javax.persistence.GeneratedValue;
5 | import javax.persistence.GenerationType;
6 | import javax.persistence.Id;
7 | import javax.persistence.ManyToOne;
8 | import javax.persistence.Table;
9 |
10 | import lombok.Getter;
11 | import lombok.Setter;
12 |
13 | @Entity
14 | @Table(name="comments")
15 | @Getter
16 | @Setter
17 | public class Comment {
18 |
19 | @Id
20 | @GeneratedValue(strategy = GenerationType.IDENTITY)
21 | private Integer id;
22 |
23 | private String content;
24 |
25 | @ManyToOne
26 | private Post post;
27 |
28 | @ManyToOne
29 | private User user;
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/com/lakshy/blog/entities/Post.java:
--------------------------------------------------------------------------------
1 | package com.lakshy.blog.entities;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Date;
5 | import java.util.List;
6 |
7 | import javax.persistence.CascadeType;
8 | import javax.persistence.Column;
9 | import javax.persistence.Entity;
10 | import javax.persistence.FetchType;
11 | import javax.persistence.GeneratedValue;
12 | import javax.persistence.GenerationType;
13 | import javax.persistence.Id;
14 | import javax.persistence.JoinColumn;
15 | import javax.persistence.ManyToOne;
16 | import javax.persistence.OneToMany;
17 | import javax.persistence.Table;
18 |
19 | import lombok.Getter;
20 | import lombok.NoArgsConstructor;
21 | import lombok.Setter;
22 |
23 | @Entity
24 | @Table(name = "posts")
25 | @Getter
26 | @Setter
27 | @NoArgsConstructor
28 | public class Post
29 | {
30 | @Id
31 | @GeneratedValue(strategy = GenerationType.IDENTITY)
32 | private Integer postId;
33 |
34 | @Column(nullable=false)
35 | private String title;
36 |
37 | @Column(length = 10000, nullable=false)
38 | private String content;
39 |
40 | private String imageName;
41 |
42 | private Date addedDate;
43 |
44 | @ManyToOne
45 | @JoinColumn(name = "category_id")
46 | private Category category;
47 |
48 | @ManyToOne
49 | private User user;
50 |
51 | @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
52 | private List comments = new ArrayList<>();
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/com/lakshy/blog/entities/Role.java:
--------------------------------------------------------------------------------
1 | package com.lakshy.blog.entities;
2 |
3 | import javax.persistence.Entity;
4 | import javax.persistence.GeneratedValue;
5 | import javax.persistence.GenerationType;
6 | import javax.persistence.Id;
7 | import javax.persistence.Table;
8 |
9 | import lombok.Getter;
10 | import lombok.Setter;
11 |
12 | @Entity
13 | @Table(name = "Roles")
14 | @Getter
15 | @Setter
16 | public class Role {
17 | @Id
18 | private int id;
19 |
20 | private String name;
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/lakshy/blog/entities/User.java:
--------------------------------------------------------------------------------
1 | package com.lakshy.blog.entities;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collection;
5 | import java.util.HashSet;
6 | import java.util.List;
7 | import java.util.Set;
8 | import java.util.stream.Collectors;
9 |
10 | import javax.persistence.CascadeType;
11 | import javax.persistence.Column;
12 | import javax.persistence.Entity;
13 | import javax.persistence.FetchType;
14 | import javax.persistence.GeneratedValue;
15 | import javax.persistence.GenerationType;
16 | import javax.persistence.Id;
17 | import javax.persistence.JoinColumn;
18 | import javax.persistence.JoinTable;
19 | import javax.persistence.ManyToMany;
20 | import javax.persistence.ManyToOne;
21 | import javax.persistence.OneToMany;
22 | import javax.persistence.Table;
23 |
24 | import org.springframework.security.core.GrantedAuthority;
25 | import org.springframework.security.core.authority.SimpleGrantedAuthority;
26 | import org.springframework.security.core.userdetails.UserDetails;
27 |
28 | import lombok.Getter;
29 | import lombok.NoArgsConstructor;
30 | import lombok.Setter;
31 |
32 | @Entity
33 | @Table(name="users")
34 | @NoArgsConstructor
35 | @Getter
36 | @Setter
37 | public class User implements UserDetails
38 | {
39 | @Id
40 | @GeneratedValue(strategy = GenerationType.IDENTITY)
41 | private Integer id;
42 |
43 | @Column(name="user_name", nullable = false, length = 100)
44 | private String name;
45 |
46 | private String email;
47 |
48 | private String password;
49 |
50 | private String about;
51 |
52 | @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
53 | private List posts = new ArrayList<>();
54 |
55 | @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
56 | private List comments = new ArrayList<>();
57 |
58 | @ManyToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
59 | @JoinTable(name = "user_role",
60 | joinColumns=@JoinColumn(name="user",referencedColumnName = "id"),
61 | inverseJoinColumns = @JoinColumn(name="role", referencedColumnName = "id")
62 | )
63 | private Set roles= new HashSet<>();
64 |
65 | // methods from UserDetails as required by Spring Security
66 | @Override
67 | public Collection extends GrantedAuthority> getAuthorities() {
68 | List authorities = this.roles.stream().map((role) -> new SimpleGrantedAuthority(role.getName())).collect(Collectors.toList());
69 | return authorities;
70 | }
71 |
72 | @Override
73 | public String getUsername() {
74 | return this.email;
75 | }
76 |
77 | @Override
78 | public boolean isAccountNonExpired() {
79 | return true;
80 | }
81 |
82 | @Override
83 | public boolean isAccountNonLocked() {
84 | return true;
85 | }
86 |
87 | @Override
88 | public boolean isCredentialsNonExpired() {
89 | return true;
90 | }
91 |
92 | @Override
93 | public boolean isEnabled() {
94 | return true;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/main/java/com/lakshy/blog/exceptions/ApiException.java:
--------------------------------------------------------------------------------
1 | package com.lakshy.blog.exceptions;
2 |
3 | public class ApiException extends RuntimeException {
4 |
5 | public ApiException(String message) {
6 | super(message);
7 |
8 | }
9 |
10 | public ApiException() {
11 | super();
12 | }
13 |
14 |
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/lakshy/blog/exceptions/GlobalExceptionHandler.java:
--------------------------------------------------------------------------------
1 | package com.lakshy.blog.exceptions;
2 |
3 | import java.util.HashMap;
4 | import java.util.List;
5 | import java.util.Map;
6 |
7 | import org.springframework.http.HttpStatus;
8 | import org.springframework.http.ResponseEntity;
9 | import org.springframework.validation.FieldError;
10 | import org.springframework.web.HttpRequestMethodNotSupportedException;
11 | import org.springframework.web.bind.MethodArgumentNotValidException;
12 | import org.springframework.web.bind.annotation.ExceptionHandler;
13 | import org.springframework.web.bind.annotation.RestControllerAdvice;
14 | import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
15 |
16 | import com.lakshy.blog.payloads.ApiResponse;
17 |
18 | // will run for all exceptions which may come for all controller classes
19 | @RestControllerAdvice
20 | public class GlobalExceptionHandler {
21 |
22 | // tell which class we want to handle response
23 | @ExceptionHandler(ResourceNotFoundException.class)
24 | public ResponseEntity resourceNotFoundExceptionHandler(ResourceNotFoundException ex)
25 | {
26 | String message = ex.getMessage();
27 | ApiResponse apiResponse = new ApiResponse(message, false);
28 |
29 | return new ResponseEntity(apiResponse,HttpStatus.NOT_FOUND);
30 | }
31 |
32 | // invalid password in login api
33 | @ExceptionHandler(ApiException.class)
34 | public ResponseEntity apiExceptionHandler(ApiException ex)
35 | {
36 | String message = ex.getMessage();
37 | ApiResponse apiResponse = new ApiResponse(message, true);
38 |
39 | return new ResponseEntity(apiResponse,HttpStatus.BAD_REQUEST);
40 | }
41 |
42 | // not a custom created exception but raised by Validator on violation
43 | @ExceptionHandler(MethodArgumentNotValidException.class)
44 | public ResponseEntity