Phoenix21 commited on
Commit
9c9f87a
·
verified ·
1 Parent(s): 4df3aa1

🗑️ Remove old backend folder

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. backend/.gitignore +0 -62
  2. backend/.mvn/wrapper/maven-wrapper.properties +0 -3
  3. backend/Dockerfile +0 -28
  4. backend/mvnw +0 -295
  5. backend/mvnw.cmd +0 -189
  6. backend/pom.xml +0 -108
  7. backend/src/main/java/com/locallend/locallend/LocallendApplication.java +0 -13
  8. backend/src/main/java/com/locallend/locallend/config/CategoryDataInitializer.java +0 -81
  9. backend/src/main/java/com/locallend/locallend/config/CloudinaryConfig.java +0 -38
  10. backend/src/main/java/com/locallend/locallend/config/CorsConfig.java +0 -63
  11. backend/src/main/java/com/locallend/locallend/config/MongoTextIndexInitializer.java +0 -41
  12. backend/src/main/java/com/locallend/locallend/config/SecurityConfig.java +0 -97
  13. backend/src/main/java/com/locallend/locallend/config/ValidationConfig.java +0 -36
  14. backend/src/main/java/com/locallend/locallend/controller/AuthController.java +0 -64
  15. backend/src/main/java/com/locallend/locallend/controller/BookingController.java +0 -261
  16. backend/src/main/java/com/locallend/locallend/controller/CategoryController.java +0 -287
  17. backend/src/main/java/com/locallend/locallend/controller/ImageController.java +0 -200
  18. backend/src/main/java/com/locallend/locallend/controller/ItemController.java +0 -349
  19. backend/src/main/java/com/locallend/locallend/controller/RatingController.java +0 -531
  20. backend/src/main/java/com/locallend/locallend/controller/UserController.java +0 -100
  21. backend/src/main/java/com/locallend/locallend/dto/CategoryDto.java +0 -106
  22. backend/src/main/java/com/locallend/locallend/dto/CreateCategoryRequest.java +0 -57
  23. backend/src/main/java/com/locallend/locallend/dto/CreateItemRequest.java +0 -49
  24. backend/src/main/java/com/locallend/locallend/dto/ItemDTO.java +0 -68
  25. backend/src/main/java/com/locallend/locallend/dto/UpdateItemRequest.java +0 -33
  26. backend/src/main/java/com/locallend/locallend/dto/request/BookingRequestDto.java +0 -78
  27. backend/src/main/java/com/locallend/locallend/dto/request/ItemRequestDto.java +0 -60
  28. backend/src/main/java/com/locallend/locallend/dto/request/ItemUpdateDto.java +0 -49
  29. backend/src/main/java/com/locallend/locallend/dto/request/RatingCreateDto.java +0 -71
  30. backend/src/main/java/com/locallend/locallend/dto/request/UserLoginDto.java +0 -33
  31. backend/src/main/java/com/locallend/locallend/dto/request/UserRegistrationDto.java +0 -71
  32. backend/src/main/java/com/locallend/locallend/dto/request/UserUpdateDto.java +0 -42
  33. backend/src/main/java/com/locallend/locallend/dto/response/BookingResponseDto.java +0 -138
  34. backend/src/main/java/com/locallend/locallend/dto/response/CategoryResponseDto.java +0 -36
  35. backend/src/main/java/com/locallend/locallend/dto/response/ItemResponseDto.java +0 -103
  36. backend/src/main/java/com/locallend/locallend/dto/response/ItemSummaryDto.java +0 -61
  37. backend/src/main/java/com/locallend/locallend/dto/response/JwtAuthResponseDto.java +0 -43
  38. backend/src/main/java/com/locallend/locallend/dto/response/RatingResponseDto.java +0 -89
  39. backend/src/main/java/com/locallend/locallend/dto/response/RatingStatsResponse.java +0 -74
  40. backend/src/main/java/com/locallend/locallend/dto/response/UserPublicDto.java +0 -76
  41. backend/src/main/java/com/locallend/locallend/dto/response/UserResponseDto.java +0 -141
  42. backend/src/main/java/com/locallend/locallend/exception/BookingConflictException.java +0 -37
  43. backend/src/main/java/com/locallend/locallend/exception/BookingNotFoundException.java +0 -32
  44. backend/src/main/java/com/locallend/locallend/exception/BusinessException.java +0 -11
  45. backend/src/main/java/com/locallend/locallend/exception/CategoryNotFoundException.java +0 -36
  46. backend/src/main/java/com/locallend/locallend/exception/DuplicateResourceException.java +0 -37
  47. backend/src/main/java/com/locallend/locallend/exception/GlobalExceptionHandler.java +0 -504
  48. backend/src/main/java/com/locallend/locallend/exception/InsufficientTrustScoreException.java +0 -31
  49. backend/src/main/java/com/locallend/locallend/exception/InvalidBookingPeriodException.java +0 -38
  50. backend/src/main/java/com/locallend/locallend/exception/InvalidOperationException.java +0 -30
backend/.gitignore DELETED
@@ -1,62 +0,0 @@
1
- # Maven
2
- target/
3
- !.mvn/wrapper/maven-wrapper.jar
4
- *.original
5
-
6
- # STS
7
- .apt_generated
8
- .classpath
9
- .factorypath
10
- .project
11
- .settings
12
- .springBeans
13
- .sts4-cache
14
-
15
- # IntelliJ IDEA
16
- .idea
17
- *.iws
18
- *.iml
19
- *.ipr
20
-
21
- # NetBeans
22
- /nbproject/private/
23
- /nbbuild/
24
- /dist/
25
- /nbdist/
26
- /.nb-gradle/
27
- build/
28
- !**/src/main/**/build/
29
- !**/src/test/**/build/
30
-
31
- # VS Code
32
- .vscode/
33
-
34
- # Environment variables
35
- .env
36
- .env.local
37
- .env.*.local
38
-
39
- # OS
40
- .DS_Store
41
- .DS_Store?
42
- ._*
43
- .Spotlight-V100
44
- .Trashes
45
- ehthumbs.db
46
- Thumbs.db
47
-
48
- # Logs
49
- *.log
50
- logs/
51
-
52
- # Compiled class files
53
- *.class
54
-
55
- # Package Files
56
- *.jar
57
- *.war
58
- *.nar
59
- *.ear
60
- *.zip
61
- *.tar.gz
62
- *.rar
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/.mvn/wrapper/maven-wrapper.properties DELETED
@@ -1,3 +0,0 @@
1
- wrapperVersion=3.3.4
2
- distributionType=only-script
3
- distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip
 
 
 
 
backend/Dockerfile DELETED
@@ -1,28 +0,0 @@
1
- # ===== STAGE 1: Build the app =====
2
- FROM maven:3.9.6-eclipse-temurin-17 AS build
3
- WORKDIR /app
4
-
5
- # Copy pom.xml and download dependencies
6
- COPY pom.xml .
7
- RUN mvn dependency:go-offline -B
8
-
9
- # Copy source and build the JAR
10
- COPY src ./src
11
- RUN mvn clean package -DskipTests
12
-
13
- # ===== STAGE 2: Run the app =====
14
- FROM eclipse-temurin:17-jre
15
- WORKDIR /app
16
-
17
- # Copy the JAR from build stage
18
- COPY --from=build /app/target/*.jar app.jar
19
-
20
- # Expose port
21
- EXPOSE 8080
22
-
23
- # Health check
24
- HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
25
- CMD curl -f http://localhost:8080/actuator/health || exit 1
26
-
27
- # Run the application
28
- ENTRYPOINT ["java", "-jar", "app.jar"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/mvnw DELETED
@@ -1,295 +0,0 @@
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
- # Apache Maven Wrapper startup batch script, version 3.3.4
23
- #
24
- # Optional ENV vars
25
- # -----------------
26
- # JAVA_HOME - location of a JDK home dir, required when download maven via java source
27
- # MVNW_REPOURL - repo url base for downloading maven distribution
28
- # MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
29
- # MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
30
- # ----------------------------------------------------------------------------
31
-
32
- set -euf
33
- [ "${MVNW_VERBOSE-}" != debug ] || set -x
34
-
35
- # OS specific support.
36
- native_path() { printf %s\\n "$1"; }
37
- case "$(uname)" in
38
- CYGWIN* | MINGW*)
39
- [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
40
- native_path() { cygpath --path --windows "$1"; }
41
- ;;
42
- esac
43
-
44
- # set JAVACMD and JAVACCMD
45
- set_java_home() {
46
- # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
47
- if [ -n "${JAVA_HOME-}" ]; then
48
- if [ -x "$JAVA_HOME/jre/sh/java" ]; then
49
- # IBM's JDK on AIX uses strange locations for the executables
50
- JAVACMD="$JAVA_HOME/jre/sh/java"
51
- JAVACCMD="$JAVA_HOME/jre/sh/javac"
52
- else
53
- JAVACMD="$JAVA_HOME/bin/java"
54
- JAVACCMD="$JAVA_HOME/bin/javac"
55
-
56
- if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
57
- echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
58
- echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
59
- return 1
60
- fi
61
- fi
62
- else
63
- JAVACMD="$(
64
- 'set' +e
65
- 'unset' -f command 2>/dev/null
66
- 'command' -v java
67
- )" || :
68
- JAVACCMD="$(
69
- 'set' +e
70
- 'unset' -f command 2>/dev/null
71
- 'command' -v javac
72
- )" || :
73
-
74
- if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
75
- echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
76
- return 1
77
- fi
78
- fi
79
- }
80
-
81
- # hash string like Java String::hashCode
82
- hash_string() {
83
- str="${1:-}" h=0
84
- while [ -n "$str" ]; do
85
- char="${str%"${str#?}"}"
86
- h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
87
- str="${str#?}"
88
- done
89
- printf %x\\n $h
90
- }
91
-
92
- verbose() { :; }
93
- [ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
94
-
95
- die() {
96
- printf %s\\n "$1" >&2
97
- exit 1
98
- }
99
-
100
- trim() {
101
- # MWRAPPER-139:
102
- # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
103
- # Needed for removing poorly interpreted newline sequences when running in more
104
- # exotic environments such as mingw bash on Windows.
105
- printf "%s" "${1}" | tr -d '[:space:]'
106
- }
107
-
108
- scriptDir="$(dirname "$0")"
109
- scriptName="$(basename "$0")"
110
-
111
- # parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
112
- while IFS="=" read -r key value; do
113
- case "${key-}" in
114
- distributionUrl) distributionUrl=$(trim "${value-}") ;;
115
- distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
116
- esac
117
- done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties"
118
- [ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
119
-
120
- case "${distributionUrl##*/}" in
121
- maven-mvnd-*bin.*)
122
- MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
123
- case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
124
- *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
125
- :Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
126
- :Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
127
- :Linux*x86_64*) distributionPlatform=linux-amd64 ;;
128
- *)
129
- echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
130
- distributionPlatform=linux-amd64
131
- ;;
132
- esac
133
- distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
134
- ;;
135
- maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
136
- *) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
137
- esac
138
-
139
- # apply MVNW_REPOURL and calculate MAVEN_HOME
140
- # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
141
- [ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
142
- distributionUrlName="${distributionUrl##*/}"
143
- distributionUrlNameMain="${distributionUrlName%.*}"
144
- distributionUrlNameMain="${distributionUrlNameMain%-bin}"
145
- MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
146
- MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
147
-
148
- exec_maven() {
149
- unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
150
- exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
151
- }
152
-
153
- if [ -d "$MAVEN_HOME" ]; then
154
- verbose "found existing MAVEN_HOME at $MAVEN_HOME"
155
- exec_maven "$@"
156
- fi
157
-
158
- case "${distributionUrl-}" in
159
- *?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
160
- *) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
161
- esac
162
-
163
- # prepare tmp dir
164
- if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
165
- clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
166
- trap clean HUP INT TERM EXIT
167
- else
168
- die "cannot create temp dir"
169
- fi
170
-
171
- mkdir -p -- "${MAVEN_HOME%/*}"
172
-
173
- # Download and Install Apache Maven
174
- verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
175
- verbose "Downloading from: $distributionUrl"
176
- verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
177
-
178
- # select .zip or .tar.gz
179
- if ! command -v unzip >/dev/null; then
180
- distributionUrl="${distributionUrl%.zip}.tar.gz"
181
- distributionUrlName="${distributionUrl##*/}"
182
- fi
183
-
184
- # verbose opt
185
- __MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
186
- [ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
187
-
188
- # normalize http auth
189
- case "${MVNW_PASSWORD:+has-password}" in
190
- '') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
191
- has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
192
- esac
193
-
194
- if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
195
- verbose "Found wget ... using wget"
196
- wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
197
- elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
198
- verbose "Found curl ... using curl"
199
- curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
200
- elif set_java_home; then
201
- verbose "Falling back to use Java to download"
202
- javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
203
- targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
204
- cat >"$javaSource" <<-END
205
- public class Downloader extends java.net.Authenticator
206
- {
207
- protected java.net.PasswordAuthentication getPasswordAuthentication()
208
- {
209
- return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
210
- }
211
- public static void main( String[] args ) throws Exception
212
- {
213
- setDefault( new Downloader() );
214
- java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
215
- }
216
- }
217
- END
218
- # For Cygwin/MinGW, switch paths to Windows format before running javac and java
219
- verbose " - Compiling Downloader.java ..."
220
- "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
221
- verbose " - Running Downloader.java ..."
222
- "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
223
- fi
224
-
225
- # If specified, validate the SHA-256 sum of the Maven distribution zip file
226
- if [ -n "${distributionSha256Sum-}" ]; then
227
- distributionSha256Result=false
228
- if [ "$MVN_CMD" = mvnd.sh ]; then
229
- echo "Checksum validation is not supported for maven-mvnd." >&2
230
- echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
231
- exit 1
232
- elif command -v sha256sum >/dev/null; then
233
- if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then
234
- distributionSha256Result=true
235
- fi
236
- elif command -v shasum >/dev/null; then
237
- if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
238
- distributionSha256Result=true
239
- fi
240
- else
241
- echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
242
- echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
243
- exit 1
244
- fi
245
- if [ $distributionSha256Result = false ]; then
246
- echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
247
- echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
248
- exit 1
249
- fi
250
- fi
251
-
252
- # unzip and move
253
- if command -v unzip >/dev/null; then
254
- unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
255
- else
256
- tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
257
- fi
258
-
259
- # Find the actual extracted directory name (handles snapshots where filename != directory name)
260
- actualDistributionDir=""
261
-
262
- # First try the expected directory name (for regular distributions)
263
- if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then
264
- if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then
265
- actualDistributionDir="$distributionUrlNameMain"
266
- fi
267
- fi
268
-
269
- # If not found, search for any directory with the Maven executable (for snapshots)
270
- if [ -z "$actualDistributionDir" ]; then
271
- # enable globbing to iterate over items
272
- set +f
273
- for dir in "$TMP_DOWNLOAD_DIR"/*; do
274
- if [ -d "$dir" ]; then
275
- if [ -f "$dir/bin/$MVN_CMD" ]; then
276
- actualDistributionDir="$(basename "$dir")"
277
- break
278
- fi
279
- fi
280
- done
281
- set -f
282
- fi
283
-
284
- if [ -z "$actualDistributionDir" ]; then
285
- verbose "Contents of $TMP_DOWNLOAD_DIR:"
286
- verbose "$(ls -la "$TMP_DOWNLOAD_DIR")"
287
- die "Could not find Maven distribution directory in extracted archive"
288
- fi
289
-
290
- verbose "Found extracted Maven distribution directory: $actualDistributionDir"
291
- printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url"
292
- mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
293
-
294
- clean || :
295
- exec_maven "$@"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/mvnw.cmd DELETED
@@ -1,189 +0,0 @@
1
- <# : batch portion
2
- @REM ----------------------------------------------------------------------------
3
- @REM Licensed to the Apache Software Foundation (ASF) under one
4
- @REM or more contributor license agreements. See the NOTICE file
5
- @REM distributed with this work for additional information
6
- @REM regarding copyright ownership. The ASF licenses this file
7
- @REM to you under the Apache License, Version 2.0 (the
8
- @REM "License"); you may not use this file except in compliance
9
- @REM with the License. You may obtain a copy of the License at
10
- @REM
11
- @REM http://www.apache.org/licenses/LICENSE-2.0
12
- @REM
13
- @REM Unless required by applicable law or agreed to in writing,
14
- @REM software distributed under the License is distributed on an
15
- @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
- @REM KIND, either express or implied. See the License for the
17
- @REM specific language governing permissions and limitations
18
- @REM under the License.
19
- @REM ----------------------------------------------------------------------------
20
-
21
- @REM ----------------------------------------------------------------------------
22
- @REM Apache Maven Wrapper startup batch script, version 3.3.4
23
- @REM
24
- @REM Optional ENV vars
25
- @REM MVNW_REPOURL - repo url base for downloading maven distribution
26
- @REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
27
- @REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
28
- @REM ----------------------------------------------------------------------------
29
-
30
- @IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
31
- @SET __MVNW_CMD__=
32
- @SET __MVNW_ERROR__=
33
- @SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
34
- @SET PSModulePath=
35
- @FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
36
- IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
37
- )
38
- @SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
39
- @SET __MVNW_PSMODULEP_SAVE=
40
- @SET __MVNW_ARG0_NAME__=
41
- @SET MVNW_USERNAME=
42
- @SET MVNW_PASSWORD=
43
- @IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*)
44
- @echo Cannot start maven from wrapper >&2 && exit /b 1
45
- @GOTO :EOF
46
- : end batch / begin powershell #>
47
-
48
- $ErrorActionPreference = "Stop"
49
- if ($env:MVNW_VERBOSE -eq "true") {
50
- $VerbosePreference = "Continue"
51
- }
52
-
53
- # calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
54
- $distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
55
- if (!$distributionUrl) {
56
- Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
57
- }
58
-
59
- switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
60
- "maven-mvnd-*" {
61
- $USE_MVND = $true
62
- $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
63
- $MVN_CMD = "mvnd.cmd"
64
- break
65
- }
66
- default {
67
- $USE_MVND = $false
68
- $MVN_CMD = $script -replace '^mvnw','mvn'
69
- break
70
- }
71
- }
72
-
73
- # apply MVNW_REPOURL and calculate MAVEN_HOME
74
- # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
75
- if ($env:MVNW_REPOURL) {
76
- $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" }
77
- $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')"
78
- }
79
- $distributionUrlName = $distributionUrl -replace '^.*/',''
80
- $distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
81
-
82
- $MAVEN_M2_PATH = "$HOME/.m2"
83
- if ($env:MAVEN_USER_HOME) {
84
- $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME"
85
- }
86
-
87
- if (-not (Test-Path -Path $MAVEN_M2_PATH)) {
88
- New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null
89
- }
90
-
91
- $MAVEN_WRAPPER_DISTS = $null
92
- if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) {
93
- $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists"
94
- } else {
95
- $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists"
96
- }
97
-
98
- $MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain"
99
- $MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
100
- $MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
101
-
102
- if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
103
- Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
104
- Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
105
- exit $?
106
- }
107
-
108
- if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
109
- Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
110
- }
111
-
112
- # prepare tmp dir
113
- $TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
114
- $TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
115
- $TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
116
- trap {
117
- if ($TMP_DOWNLOAD_DIR.Exists) {
118
- try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
119
- catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
120
- }
121
- }
122
-
123
- New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
124
-
125
- # Download and Install Apache Maven
126
- Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
127
- Write-Verbose "Downloading from: $distributionUrl"
128
- Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
129
-
130
- $webclient = New-Object System.Net.WebClient
131
- if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
132
- $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
133
- }
134
- [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
135
- $webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
136
-
137
- # If specified, validate the SHA-256 sum of the Maven distribution zip file
138
- $distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
139
- if ($distributionSha256Sum) {
140
- if ($USE_MVND) {
141
- Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
142
- }
143
- Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
144
- if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
145
- Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
146
- }
147
- }
148
-
149
- # unzip and move
150
- Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
151
-
152
- # Find the actual extracted directory name (handles snapshots where filename != directory name)
153
- $actualDistributionDir = ""
154
-
155
- # First try the expected directory name (for regular distributions)
156
- $expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain"
157
- $expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD"
158
- if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) {
159
- $actualDistributionDir = $distributionUrlNameMain
160
- }
161
-
162
- # If not found, search for any directory with the Maven executable (for snapshots)
163
- if (!$actualDistributionDir) {
164
- Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object {
165
- $testPath = Join-Path $_.FullName "bin/$MVN_CMD"
166
- if (Test-Path -Path $testPath -PathType Leaf) {
167
- $actualDistributionDir = $_.Name
168
- }
169
- }
170
- }
171
-
172
- if (!$actualDistributionDir) {
173
- Write-Error "Could not find Maven distribution directory in extracted archive"
174
- }
175
-
176
- Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir"
177
- Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null
178
- try {
179
- Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
180
- } catch {
181
- if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
182
- Write-Error "fail to move MAVEN_HOME"
183
- }
184
- } finally {
185
- try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
186
- catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
187
- }
188
-
189
- Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/pom.xml DELETED
@@ -1,108 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4
- <modelVersion>4.0.0</modelVersion>
5
- <parent>
6
- <groupId>org.springframework.boot</groupId>
7
- <artifactId>spring-boot-starter-parent</artifactId>
8
- <version>3.5.6</version>
9
- <relativePath/> <!-- lookup parent from repository -->
10
- </parent>
11
- <groupId>com.locallend</groupId>
12
- <artifactId>locallend</artifactId>
13
- <version>0.0.1-SNAPSHOT</version>
14
- <name>locallend</name>
15
- <description>Demo project for Spring Boot</description>
16
- <url/>
17
- <licenses>
18
- <license/>
19
- </licenses>
20
- <developers>
21
- <developer/>
22
- </developers>
23
- <scm>
24
- <connection/>
25
- <developerConnection/>
26
- <tag/>
27
- <url/>
28
- </scm>
29
- <properties>
30
- <java.version>17</java.version>
31
- </properties>
32
- <dependencies>
33
- <dependency>
34
- <groupId>org.springframework.boot</groupId>
35
- <artifactId>spring-boot-starter-data-mongodb</artifactId>
36
- </dependency>
37
- <dependency>
38
- <groupId>org.springframework.boot</groupId>
39
- <artifactId>spring-boot-starter-mail</artifactId>
40
- </dependency>
41
- <dependency>
42
- <groupId>org.springframework.boot</groupId>
43
- <artifactId>spring-boot-starter-security</artifactId>
44
- </dependency>
45
- <dependency>
46
- <groupId>org.springframework.boot</groupId>
47
- <artifactId>spring-boot-starter-validation</artifactId>
48
- </dependency>
49
- <dependency>
50
- <groupId>org.springframework.boot</groupId>
51
- <artifactId>spring-boot-starter-web</artifactId>
52
- </dependency>
53
-
54
- <!-- JWT for token-based authentication -->
55
- <dependency>
56
- <groupId>io.jsonwebtoken</groupId>
57
- <artifactId>jjwt-api</artifactId>
58
- <version>0.11.5</version>
59
- </dependency>
60
- <dependency>
61
- <groupId>io.jsonwebtoken</groupId>
62
- <artifactId>jjwt-impl</artifactId>
63
- <version>0.11.5</version>
64
- <scope>runtime</scope>
65
- </dependency>
66
- <dependency>
67
- <groupId>io.jsonwebtoken</groupId>
68
- <artifactId>jjwt-jackson</artifactId>
69
- <version>0.11.5</version>
70
- <scope>runtime</scope>
71
- </dependency>
72
-
73
- <dependency>
74
- <groupId>org.springframework.boot</groupId>
75
- <artifactId>spring-boot-starter-test</artifactId>
76
- <scope>test</scope>
77
- </dependency>
78
- <dependency>
79
- <groupId>org.springframework.security</groupId>
80
- <artifactId>spring-security-test</artifactId>
81
- <scope>test</scope>
82
- </dependency>
83
-
84
- <!-- Cloudinary for image upload and management -->
85
- <dependency>
86
- <groupId>com.cloudinary</groupId>
87
- <artifactId>cloudinary-http44</artifactId>
88
- <version>1.38.0</version>
89
- </dependency>
90
-
91
- <!-- dotenv for loading .env files -->
92
- <dependency>
93
- <groupId>me.paulschwarz</groupId>
94
- <artifactId>spring-dotenv</artifactId>
95
- <version>4.0.0</version>
96
- </dependency>
97
- </dependencies>
98
-
99
- <build>
100
- <plugins>
101
- <plugin>
102
- <groupId>org.springframework.boot</groupId>
103
- <artifactId>spring-boot-maven-plugin</artifactId>
104
- </plugin>
105
- </plugins>
106
- </build>
107
-
108
- </project>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/LocallendApplication.java DELETED
@@ -1,13 +0,0 @@
1
- package com.locallend.locallend;
2
-
3
- import org.springframework.boot.SpringApplication;
4
- import org.springframework.boot.autoconfigure.SpringBootApplication;
5
-
6
- @SpringBootApplication
7
- public class LocallendApplication {
8
-
9
- public static void main(String[] args) {
10
- SpringApplication.run(LocallendApplication.class, args);
11
- }
12
-
13
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/config/CategoryDataInitializer.java DELETED
@@ -1,81 +0,0 @@
1
- package com.locallend.locallend.config;
2
-
3
- import com.locallend.locallend.model.Category;
4
- import com.locallend.locallend.repository.CategoryRepository;
5
- import org.slf4j.Logger;
6
- import org.slf4j.LoggerFactory;
7
- import org.springframework.boot.ApplicationArguments;
8
- import org.springframework.boot.ApplicationRunner;
9
- import org.springframework.context.annotation.Profile;
10
- import org.springframework.stereotype.Component;
11
-
12
- import java.time.LocalDateTime;
13
- import java.util.Arrays;
14
- import java.util.List;
15
-
16
- /**
17
- * Initializes default categories in the database at startup.
18
- * This ensures that the application has basic categories available
19
- * immediately after deployment without manual intervention.
20
- */
21
- @Component
22
- @Profile("prod") // Only run in production profile used in docker-compose
23
- public class CategoryDataInitializer implements ApplicationRunner {
24
-
25
- private static final Logger log = LoggerFactory.getLogger(CategoryDataInitializer.class);
26
- private final CategoryRepository categoryRepository;
27
-
28
- public CategoryDataInitializer(CategoryRepository categoryRepository) {
29
- this.categoryRepository = categoryRepository;
30
- }
31
-
32
- @Override
33
- public void run(ApplicationArguments args) {
34
- initializeDefaultCategories();
35
- }
36
-
37
- private void initializeDefaultCategories() {
38
- // Check if categories already exist to avoid duplicates
39
- long existingCategoryCount = categoryRepository.count();
40
- if (existingCategoryCount > 0) {
41
- log.info("[CategoryInit] Categories already exist ({}), skipping initialization", existingCategoryCount);
42
- return;
43
- }
44
-
45
- log.info("[CategoryInit] Initializing default categories...");
46
-
47
- List<Category> defaultCategories = Arrays.asList(
48
- createCategory("Electronics", "Electronic devices, gadgets, and technology items including phones, laptops, cameras, and accessories"),
49
- createCategory("Tools", "Hand tools, power tools, and equipment for construction, repair, and maintenance work"),
50
- createCategory("Sports", "Sports equipment, gear, and accessories for various athletic activities and outdoor recreation"),
51
- createCategory("Books", "Books, magazines, educational materials, and reading resources across all genres and topics"),
52
- createCategory("Furniture", "Home and office furniture including chairs, tables, storage solutions, and decorative pieces"),
53
- createCategory("Vehicles", "Cars, motorcycles, bicycles, and other transportation vehicles and related accessories"),
54
- createCategory("Appliances", "Home appliances, kitchen equipment, and household electrical devices"),
55
- createCategory("Others", "Miscellaneous items that don't fit into other specific categories")
56
- );
57
-
58
- try {
59
- List<Category> savedCategories = categoryRepository.saveAll(defaultCategories);
60
- log.info("[CategoryInit] Successfully initialized {} categories", savedCategories.size());
61
-
62
- // Log each category for verification
63
- savedCategories.forEach(category ->
64
- log.info("[CategoryInit] Created category: {} (ID: {})", category.getName(), category.getId())
65
- );
66
-
67
- } catch (Exception e) {
68
- log.error("[CategoryInit] Failed to initialize categories", e);
69
- throw new RuntimeException("Failed to initialize default categories", e);
70
- }
71
- }
72
-
73
- private Category createCategory(String name, String description) {
74
- Category category = new Category();
75
- category.setName(name);
76
- category.setDescription(description);
77
- category.setCreatedAt(LocalDateTime.now());
78
- category.setUpdatedAt(LocalDateTime.now());
79
- return category;
80
- }
81
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/config/CloudinaryConfig.java DELETED
@@ -1,38 +0,0 @@
1
- package com.locallend.locallend.config;
2
-
3
- import com.cloudinary.Cloudinary;
4
- import com.cloudinary.utils.ObjectUtils;
5
- import org.springframework.beans.factory.annotation.Value;
6
- import org.springframework.context.annotation.Bean;
7
- import org.springframework.context.annotation.Configuration;
8
-
9
- /**
10
- * Configuration class for Cloudinary integration.
11
- * Initializes Cloudinary instance with credentials from application.properties.
12
- */
13
- @Configuration
14
- public class CloudinaryConfig {
15
-
16
- @Value("${cloudinary.cloud-name}")
17
- private String cloudName;
18
-
19
- @Value("${cloudinary.api-key}")
20
- private String apiKey;
21
-
22
- @Value("${cloudinary.api-secret}")
23
- private String apiSecret;
24
-
25
- /**
26
- * Creates and configures a Cloudinary bean.
27
- * @return Configured Cloudinary instance
28
- */
29
- @Bean
30
- public Cloudinary cloudinary() {
31
- return new Cloudinary(ObjectUtils.asMap(
32
- "cloud_name", cloudName,
33
- "api_key", apiKey,
34
- "api_secret", apiSecret,
35
- "secure", true
36
- ));
37
- }
38
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/config/CorsConfig.java DELETED
@@ -1,63 +0,0 @@
1
- package com.locallend.locallend.config;
2
-
3
- import org.springframework.context.annotation.Bean;
4
- import org.springframework.context.annotation.Configuration;
5
- import org.springframework.web.cors.CorsConfiguration;
6
- import org.springframework.web.cors.CorsConfigurationSource;
7
- import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
8
-
9
- import java.util.Arrays;
10
- import java.util.List;
11
-
12
- /**
13
- * CORS (Cross-Origin Resource Sharing) configuration for the LocalLend application.
14
- * Enables the frontend to communicate with the backend from different origins.
15
- */
16
- @Configuration
17
- public class CorsConfig {
18
-
19
- /**
20
- * CORS configuration source that defines allowed origins, methods, and headers.
21
- * This enables the React frontend (localhost:5173) to communicate with the backend (localhost:8080).
22
- *
23
- * @return CorsConfigurationSource with appropriate CORS settings
24
- */
25
- @Bean
26
- public CorsConfigurationSource corsConfigurationSource() {
27
- CorsConfiguration configuration = new CorsConfiguration();
28
-
29
- // Allow requests from frontend origins
30
- configuration.setAllowedOriginPatterns(Arrays.asList(
31
- "http://localhost", // Production (Nginx on port 80)
32
- "http://localhost:80", // Production (explicit port 80)
33
- "http://localhost:3000", // Create React App default
34
- "http://localhost:5173", // Vite default port
35
- "http://127.0.0.1", // Production (alternative localhost)
36
- "http://127.0.0.1:80", // Production (alternative localhost port 80)
37
- "http://127.0.0.1:3000", // Alternative localhost
38
- "http://127.0.0.1:5173", // Alternative localhost
39
- "https://*.hf.space", // Hugging Face Spaces
40
- "https://*.huggingface.co" // Hugging Face domain
41
- ));
42
-
43
- // Allow specific HTTP methods
44
- configuration.setAllowedMethods(Arrays.asList(
45
- "GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"
46
- ));
47
-
48
- // Allow all headers (including custom headers like Authorization)
49
- configuration.setAllowedHeaders(List.of("*"));
50
-
51
- // Allow credentials (cookies, authorization headers, etc.)
52
- configuration.setAllowCredentials(true);
53
-
54
- // Cache preflight requests for 1 hour
55
- configuration.setMaxAge(3600L);
56
-
57
- // Apply this configuration to all endpoints
58
- UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
59
- source.registerCorsConfiguration("/**", configuration);
60
-
61
- return source;
62
- }
63
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/config/MongoTextIndexInitializer.java DELETED
@@ -1,41 +0,0 @@
1
- package com.locallend.locallend.config;
2
-
3
- import com.locallend.locallend.model.Category;
4
- import org.slf4j.Logger;
5
- import org.slf4j.LoggerFactory;
6
- import org.springframework.boot.ApplicationArguments;
7
- import org.springframework.boot.ApplicationRunner;
8
- import org.springframework.context.annotation.Profile;
9
- import org.springframework.data.mongodb.core.MongoTemplate;
10
- import org.springframework.data.mongodb.core.index.TextIndexDefinition;
11
- import org.springframework.stereotype.Component;
12
-
13
-
14
- /**
15
- * Ensures required MongoDB text indexes exist at startup so that search endpoints work
16
- * on a freshly created database without manual intervention.
17
- */
18
- @Component
19
- @Profile("prod") // limit to prod profile used in docker-compose
20
- public class MongoTextIndexInitializer implements ApplicationRunner {
21
- private static final Logger log = LoggerFactory.getLogger(MongoTextIndexInitializer.class);
22
- private final MongoTemplate mongoTemplate;
23
-
24
- public MongoTextIndexInitializer(MongoTemplate mongoTemplate) {
25
- this.mongoTemplate = mongoTemplate;
26
- }
27
-
28
- @Override
29
- public void run(ApplicationArguments args) {
30
- ensureCategoryTextIndex();
31
- }
32
-
33
- private void ensureCategoryTextIndex() {
34
- TextIndexDefinition def = new TextIndexDefinition.TextIndexDefinitionBuilder()
35
- .onField("name")
36
- .onField("description")
37
- .build();
38
- String indexName = mongoTemplate.indexOps(Category.class).createIndex(def);
39
- log.info("[MongoIndex] Ensured Category text index (name='{}') on fields: name, description", indexName);
40
- }
41
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/config/SecurityConfig.java DELETED
@@ -1,97 +0,0 @@
1
- package com.locallend.locallend.config;
2
-
3
- import com.locallend.locallend.security.JwtAuthenticationFilter;
4
- import com.locallend.locallend.security.JwtTokenProvider;
5
- import com.locallend.locallend.security.MongoUserDetailsService;
6
- import org.springframework.context.annotation.Bean;
7
- import org.springframework.context.annotation.Configuration;
8
- import org.springframework.http.HttpMethod;
9
- import org.springframework.security.authentication.AuthenticationManager;
10
- import org.springframework.security.config.Customizer;
11
- import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
12
- import org.springframework.security.config.annotation.web.builders.HttpSecurity;
13
- import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
14
- import org.springframework.security.config.http.SessionCreationPolicy;
15
- import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
16
- import org.springframework.security.crypto.password.PasswordEncoder;
17
- import org.springframework.security.web.SecurityFilterChain;
18
- import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
19
-
20
- /**
21
- * Spring Security configuration with JWT authentication.
22
- * Protects endpoints while allowing public access to auth endpoints.
23
- */
24
- @Configuration
25
- public class SecurityConfig {
26
-
27
- private final MongoUserDetailsService userDetailsService;
28
- private final JwtTokenProvider jwtTokenProvider;
29
-
30
- public SecurityConfig(MongoUserDetailsService userDetailsService,
31
- JwtTokenProvider jwtTokenProvider) {
32
- this.userDetailsService = userDetailsService;
33
- this.jwtTokenProvider = jwtTokenProvider;
34
- }
35
-
36
- /**
37
- * Password encoder bean using BCrypt.
38
- * @return BCryptPasswordEncoder instance
39
- */
40
- @Bean
41
- public PasswordEncoder passwordEncoder() {
42
- return new BCryptPasswordEncoder();
43
- }
44
-
45
- /**
46
- * Authentication manager bean for manual authentication.
47
- * @param config Authentication configuration
48
- * @return AuthenticationManager
49
- * @throws Exception if configuration fails
50
- */
51
- @Bean
52
- public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
53
- return config.getAuthenticationManager();
54
- }
55
-
56
- /**
57
- * JWT authentication filter bean.
58
- * @return JwtAuthenticationFilter instance
59
- */
60
- @Bean
61
- public JwtAuthenticationFilter jwtAuthenticationFilter() {
62
- return new JwtAuthenticationFilter(jwtTokenProvider, userDetailsService);
63
- }
64
-
65
- /**
66
- * Security filter chain configuration.
67
- * Defines endpoint protection rules and integrates JWT filter.
68
- * @param http HttpSecurity
69
- * @return SecurityFilterChain
70
- * @throws Exception if configuration fails
71
- */
72
- @Bean
73
- public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
74
- http
75
- .csrf(AbstractHttpConfigurer::disable)
76
- .cors(Customizer.withDefaults())
77
- .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
78
- .authorizeHttpRequests(auth -> auth
79
- // Public endpoints - no authentication required
80
- .requestMatchers(HttpMethod.POST, "/api/auth/register").permitAll()
81
- .requestMatchers(HttpMethod.POST, "/api/auth/login").permitAll()
82
-
83
- // Public category and item browsing
84
- .requestMatchers(HttpMethod.GET, "/api/categories/**").permitAll()
85
- .requestMatchers(HttpMethod.GET, "/api/items/**").permitAll()
86
- .requestMatchers(HttpMethod.GET, "/api/users/*/public").permitAll()
87
- .requestMatchers(HttpMethod.GET, "/api/users/search").permitAll()
88
-
89
- // All other endpoints require authentication
90
- .anyRequest().authenticated()
91
- )
92
- // Add JWT filter before Spring Security's authentication filter
93
- .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
94
-
95
- return http.build();
96
- }
97
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/config/ValidationConfig.java DELETED
@@ -1,36 +0,0 @@
1
- package com.locallend.locallend.config;
2
-
3
- import org.springframework.context.annotation.Bean;
4
- import org.springframework.context.annotation.Configuration;
5
- import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
6
-
7
- import jakarta.validation.Validator;
8
-
9
- /**
10
- * Configuration for Jakarta Bean Validation (JSR-380).
11
- * Ensures validation annotations on entities and DTOs are processed.
12
- * Issue #29: Production-ready validation support
13
- */
14
- @Configuration
15
- public class ValidationConfig {
16
-
17
- /**
18
- * Configure validator factory bean.
19
- * Enables @Valid, @NotBlank, @Size, @Email, @Pattern, etc. annotations
20
- * @return LocalValidatorFactoryBean instance
21
- */
22
- @Bean
23
- public LocalValidatorFactoryBean validator() {
24
- return new LocalValidatorFactoryBean();
25
- }
26
-
27
- /**
28
- * Provide Validator bean for programmatic validation.
29
- * Can be injected into services for manual validation if needed.
30
- * @return Validator instance
31
- */
32
- @Bean
33
- public Validator validatorBean() {
34
- return validator();
35
- }
36
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/controller/AuthController.java DELETED
@@ -1,64 +0,0 @@
1
- package com.locallend.locallend.controller;
2
-
3
- import com.locallend.locallend.dto.request.UserLoginDto;
4
- import com.locallend.locallend.dto.request.UserRegistrationDto;
5
- import com.locallend.locallend.dto.response.JwtAuthResponseDto;
6
- import com.locallend.locallend.dto.response.UserResponseDto;
7
- import com.locallend.locallend.model.User;
8
- import com.locallend.locallend.security.JwtTokenProvider;
9
- import com.locallend.locallend.service.UserService;
10
- import com.locallend.locallend.util.UserMapper;
11
- import jakarta.validation.Valid;
12
- import org.springframework.beans.factory.annotation.Autowired;
13
- import org.springframework.http.HttpStatus;
14
- import org.springframework.http.ResponseEntity;
15
- import org.springframework.web.bind.annotation.*;
16
-
17
- /**
18
- * REST controller for authentication operations.
19
- * Handles user registration and login with JWT token generation.
20
- */
21
- @RestController
22
- @RequestMapping("/api/auth")
23
- public class AuthController {
24
-
25
- @Autowired
26
- private UserService userService;
27
-
28
- @Autowired
29
- private UserMapper userMapper;
30
-
31
- @Autowired
32
- private JwtTokenProvider jwtTokenProvider;
33
-
34
- /**
35
- * Register a new user.
36
- * @param dto User registration data
37
- * @return Created user response DTO
38
- */
39
- @PostMapping("/register")
40
- public ResponseEntity<UserResponseDto> register(@Valid @RequestBody UserRegistrationDto dto) {
41
- User user = userService.registerUser(dto);
42
- UserResponseDto response = userMapper.toUserResponseDto(user);
43
- return ResponseEntity.status(HttpStatus.CREATED).body(response);
44
- }
45
-
46
- /**
47
- * Authenticate user and login with JWT token.
48
- * @param dto Login credentials (username/email and password)
49
- * @return JWT token and user information
50
- */
51
- @PostMapping("/login")
52
- public ResponseEntity<JwtAuthResponseDto> login(@Valid @RequestBody UserLoginDto dto) {
53
- User user = userService.verifyCredentials(dto.getUsernameOrEmail(), dto.getPassword());
54
-
55
- // Generate JWT token
56
- String token = jwtTokenProvider.generateToken(user.getUsername());
57
-
58
- // Prepare response with token and user data
59
- UserResponseDto userResponse = userMapper.toUserResponseDto(user);
60
- JwtAuthResponseDto response = new JwtAuthResponseDto(token, userResponse);
61
-
62
- return ResponseEntity.ok(response);
63
- }
64
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/controller/BookingController.java DELETED
@@ -1,261 +0,0 @@
1
- package com.locallend.locallend.controller;
2
-
3
- import com.locallend.locallend.dto.request.BookingRequestDto;
4
- import com.locallend.locallend.dto.response.BookingResponseDto;
5
- import com.locallend.locallend.exception.BookingConflictException;
6
- import com.locallend.locallend.exception.BookingNotFoundException;
7
- import com.locallend.locallend.exception.InvalidBookingPeriodException;
8
- import com.locallend.locallend.service.BookingService;
9
- import jakarta.validation.Valid;
10
- import org.springframework.http.HttpStatus;
11
- import org.springframework.http.ResponseEntity;
12
- import org.springframework.web.bind.annotation.*;
13
-
14
- import java.util.HashMap;
15
- import java.util.List;
16
- import java.util.Map;
17
-
18
- @RestController
19
- @RequestMapping("/api/bookings")
20
- @CrossOrigin(origins = "*", maxAge = 3600)
21
- public class BookingController {
22
-
23
- private final BookingService bookingService;
24
-
25
- public BookingController(BookingService bookingService) {
26
- this.bookingService = bookingService;
27
- }
28
-
29
- @PostMapping
30
- public ResponseEntity<?> createBooking(@Valid @RequestBody BookingRequestDto request,
31
- @RequestHeader("X-User-Id") String borrowerId) {
32
- try {
33
- BookingResponseDto dto = bookingService.createBooking(request, borrowerId);
34
- return ResponseEntity.status(HttpStatus.CREATED).body(dto);
35
- } catch (BookingConflictException e) {
36
- return error(HttpStatus.CONFLICT, "Booking conflict", e.getMessage(), "BOOKING_CONFLICT");
37
- } catch (InvalidBookingPeriodException e) {
38
- return error(HttpStatus.BAD_REQUEST, "Invalid booking period", e.getMessage(), "INVALID_PERIOD");
39
- } catch (IllegalArgumentException | IllegalStateException e) {
40
- return error(HttpStatus.BAD_REQUEST, "Booking creation failed", e.getMessage(), "BOOKING_CREATE_ERROR");
41
- } catch (Exception e) {
42
- return error(HttpStatus.INTERNAL_SERVER_ERROR, "Unexpected error", e.getMessage(), "INTERNAL_ERROR");
43
- }
44
- }
45
-
46
- @GetMapping("/{id}")
47
- public ResponseEntity<?> getBooking(@PathVariable String id) {
48
- try {
49
- BookingResponseDto dto = bookingService.getBookingById(id);
50
- return ResponseEntity.ok(dto);
51
- } catch (BookingNotFoundException e) {
52
- return error(HttpStatus.NOT_FOUND, "Booking not found", e.getMessage(), "BOOKING_NOT_FOUND");
53
- }
54
- }
55
-
56
- /**
57
- * Get borrower's bookings (Issue #15 naming)
58
- */
59
- @GetMapping("/my-bookings")
60
- public ResponseEntity<?> myBookings(@RequestHeader("X-User-Id") String userId) {
61
- List<BookingResponseDto> list = bookingService.getBookingsForBorrower(userId);
62
- return ResponseEntity.ok(list);
63
- }
64
-
65
- /**
66
- * Get borrower's bookings (original endpoint)
67
- */
68
- @GetMapping("/my")
69
- public ResponseEntity<?> myBookingsOriginal(@RequestHeader("X-User-Id") String userId) {
70
- List<BookingResponseDto> list = bookingService.getBookingsForBorrower(userId);
71
- return ResponseEntity.ok(list);
72
- }
73
-
74
- /**
75
- * Get pending approvals for owner (Issue #15 naming)
76
- */
77
- @GetMapping("/pending-approvals")
78
- public ResponseEntity<?> pendingApprovals(@RequestHeader("X-User-Id") String ownerId) {
79
- try {
80
- com.locallend.locallend.model.enums.BookingStatus pendingStatus =
81
- com.locallend.locallend.model.enums.BookingStatus.PENDING;
82
- List<BookingResponseDto> bookings = bookingService.getBookingsForOwner(ownerId);
83
- // Filter for PENDING status
84
- List<BookingResponseDto> pending = bookings.stream()
85
- .filter(b -> b.getStatus() == pendingStatus)
86
- .toList();
87
- return ResponseEntity.ok(pending);
88
- } catch (Exception e) {
89
- return error(HttpStatus.INTERNAL_SERVER_ERROR, "Error fetching pending approvals", e.getMessage(), "FETCH_ERROR");
90
- }
91
- }
92
-
93
- /**
94
- * Get owner's bookings (original endpoint)
95
- */
96
- @GetMapping("/my-owned")
97
- public ResponseEntity<?> myOwnedBookings(@RequestHeader("X-User-Id") String ownerId) {
98
- List<BookingResponseDto> list = bookingService.getBookingsForOwner(ownerId);
99
- return ResponseEntity.ok(list);
100
- }
101
-
102
- /**
103
- * Approve booking - Issue #15 naming (alias for confirm)
104
- */
105
- @PatchMapping("/{id}/approve")
106
- public ResponseEntity<?> approve(@PathVariable String id,
107
- @RequestHeader("X-User-Id") String ownerId,
108
- @RequestBody(required = false) Map<String, String> body) {
109
- String notes = body != null ? body.get("ownerNotes") : null;
110
- try {
111
- BookingResponseDto dto = bookingService.confirm(id, ownerId, notes);
112
- return ResponseEntity.ok(dto);
113
- } catch (BookingNotFoundException e) {
114
- return error(HttpStatus.NOT_FOUND, "Booking not found", e.getMessage(), "BOOKING_NOT_FOUND");
115
- } catch (IllegalArgumentException | IllegalStateException e) {
116
- return error(HttpStatus.BAD_REQUEST, "Cannot approve booking", e.getMessage(), "BOOKING_APPROVE_ERROR");
117
- }
118
- }
119
-
120
- /**
121
- * Confirm booking - original endpoint
122
- */
123
- @PatchMapping("/{id}/confirm")
124
- public ResponseEntity<?> confirm(@PathVariable String id,
125
- @RequestHeader("X-User-Id") String ownerId,
126
- @RequestBody(required = false) Map<String, String> body) {
127
- String notes = body != null ? body.get("ownerNotes") : null;
128
- try {
129
- BookingResponseDto dto = bookingService.confirm(id, ownerId, notes);
130
- return ResponseEntity.ok(dto);
131
- } catch (BookingNotFoundException e) {
132
- return error(HttpStatus.NOT_FOUND, "Booking not found", e.getMessage(), "BOOKING_NOT_FOUND");
133
- } catch (IllegalArgumentException | IllegalStateException e) {
134
- return error(HttpStatus.BAD_REQUEST, "Cannot confirm booking", e.getMessage(), "BOOKING_CONFIRM_ERROR");
135
- }
136
- }
137
-
138
- /**
139
- * Start booking - Issue #15 naming (alias for activate)
140
- */
141
- @PatchMapping("/{id}/start")
142
- public ResponseEntity<?> start(@PathVariable String id,
143
- @RequestHeader("X-User-Id") String borrowerId) {
144
- try {
145
- BookingResponseDto dto = bookingService.activate(id, borrowerId);
146
- return ResponseEntity.ok(dto);
147
- } catch (BookingNotFoundException e) {
148
- return error(HttpStatus.NOT_FOUND, "Booking not found", e.getMessage(), "BOOKING_NOT_FOUND");
149
- } catch (IllegalArgumentException | IllegalStateException e) {
150
- return error(HttpStatus.BAD_REQUEST, "Cannot start booking", e.getMessage(), "BOOKING_START_ERROR");
151
- }
152
- }
153
-
154
- /**
155
- * Activate booking - original endpoint
156
- */
157
- @PatchMapping("/{id}/activate")
158
- public ResponseEntity<?> activate(@PathVariable String id,
159
- @RequestHeader("X-User-Id") String borrowerId) {
160
- try {
161
- BookingResponseDto dto = bookingService.activate(id, borrowerId);
162
- return ResponseEntity.ok(dto);
163
- } catch (BookingNotFoundException e) {
164
- return error(HttpStatus.NOT_FOUND, "Booking not found", e.getMessage(), "BOOKING_NOT_FOUND");
165
- } catch (IllegalArgumentException | IllegalStateException e) {
166
- return error(HttpStatus.BAD_REQUEST, "Cannot activate booking", e.getMessage(), "BOOKING_ACTIVATE_ERROR");
167
- }
168
- }
169
-
170
- @PatchMapping("/{id}/complete")
171
- public ResponseEntity<?> complete(@PathVariable String id,
172
- @RequestHeader("X-User-Id") String borrowerId) {
173
- try {
174
- BookingResponseDto dto = bookingService.complete(id, borrowerId);
175
- return ResponseEntity.ok(dto);
176
- } catch (BookingNotFoundException e) {
177
- return error(HttpStatus.NOT_FOUND, "Booking not found", e.getMessage(), "BOOKING_NOT_FOUND");
178
- } catch (IllegalArgumentException | IllegalStateException e) {
179
- return error(HttpStatus.BAD_REQUEST, "Cannot complete booking", e.getMessage(), "BOOKING_COMPLETE_ERROR");
180
- }
181
- }
182
-
183
- @PatchMapping("/{id}/cancel")
184
- public ResponseEntity<?> cancel(@PathVariable String id,
185
- @RequestHeader("X-User-Id") String borrowerId,
186
- @RequestBody(required = false) Map<String, String> body) {
187
- String reason = body != null ? body.get("reason") : null;
188
- try {
189
- BookingResponseDto dto = bookingService.cancel(id, borrowerId, reason);
190
- return ResponseEntity.ok(dto);
191
- } catch (BookingNotFoundException e) {
192
- return error(HttpStatus.NOT_FOUND, "Booking not found", e.getMessage(), "BOOKING_NOT_FOUND");
193
- } catch (IllegalArgumentException | IllegalStateException e) {
194
- return error(HttpStatus.BAD_REQUEST, "Cannot cancel booking", e.getMessage(), "BOOKING_CANCEL_ERROR");
195
- }
196
- }
197
-
198
- @PatchMapping("/{id}/reject")
199
- public ResponseEntity<?> reject(@PathVariable String id,
200
- @RequestHeader("X-User-Id") String ownerId,
201
- @RequestBody(required = false) Map<String, String> body) {
202
- String reason = body != null ? body.get("reason") : null;
203
- try {
204
- BookingResponseDto dto = bookingService.reject(id, ownerId, reason);
205
- return ResponseEntity.ok(dto);
206
- } catch (BookingNotFoundException e) {
207
- return error(HttpStatus.NOT_FOUND, "Booking not found", e.getMessage(), "BOOKING_NOT_FOUND");
208
- } catch (IllegalArgumentException | IllegalStateException e) {
209
- return error(HttpStatus.BAD_REQUEST, "Cannot reject booking", e.getMessage(), "BOOKING_REJECT_ERROR");
210
- }
211
- }
212
-
213
- @GetMapping("/active")
214
- public ResponseEntity<?> activeBookings() {
215
- return ResponseEntity.ok(bookingService.getActiveBookings());
216
- }
217
-
218
- @GetMapping("/overdue")
219
- public ResponseEntity<?> overdueBookings() {
220
- return ResponseEntity.ok(bookingService.getOverdueBookings());
221
- }
222
-
223
-
224
-
225
- @GetMapping("/by-status")
226
- public ResponseEntity<?> bookingsByStatus(@RequestParam String status,
227
- @RequestHeader("X-User-Id") String userId) {
228
- try {
229
- com.locallend.locallend.model.enums.BookingStatus bookingStatus =
230
- com.locallend.locallend.model.enums.BookingStatus.fromString(status);
231
- List<BookingResponseDto> bookings = bookingService.getBookingsByStatus(bookingStatus, userId);
232
- return ResponseEntity.ok(bookings);
233
- } catch (IllegalArgumentException e) {
234
- return error(HttpStatus.BAD_REQUEST, "Invalid status", e.getMessage(), "INVALID_STATUS");
235
- }
236
- }
237
-
238
- @GetMapping("/item/{itemId}/active")
239
- public ResponseEntity<?> activeBookingsForItem(@PathVariable String itemId) {
240
- return ResponseEntity.ok(bookingService.getActiveBookingsForItem(itemId));
241
- }
242
-
243
- @PostMapping("/process-overdue")
244
- public ResponseEntity<?> processOverdue() {
245
- int count = bookingService.processOverdueBookings();
246
- Map<String, Object> resp = new HashMap<>();
247
- resp.put("success", true);
248
- resp.put("message", "Processed overdue bookings");
249
- resp.put("count", count);
250
- return ResponseEntity.ok(resp);
251
- }
252
-
253
- private ResponseEntity<Map<String, Object>> error(HttpStatus status, String message, String details, String code) {
254
- Map<String, Object> resp = new HashMap<>();
255
- resp.put("success", false);
256
- resp.put("message", message);
257
- resp.put("details", details);
258
- resp.put("error_code", code);
259
- return ResponseEntity.status(status).body(resp);
260
- }
261
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/controller/CategoryController.java DELETED
@@ -1,287 +0,0 @@
1
- package com.locallend.locallend.controller;
2
-
3
- import com.locallend.locallend.dto.CategoryDto;
4
- import com.locallend.locallend.dto.CreateCategoryRequest;
5
- import com.locallend.locallend.exception.CategoryNotFoundException;
6
- import com.locallend.locallend.service.CategoryService;
7
-
8
- import org.slf4j.Logger;
9
- import org.slf4j.LoggerFactory;
10
- import org.springframework.http.HttpStatus;
11
- import org.springframework.http.ResponseEntity;
12
- import org.springframework.web.bind.annotation.*;
13
- import org.springframework.validation.annotation.Validated;
14
-
15
- import jakarta.validation.Valid;
16
- import jakarta.validation.constraints.NotBlank;
17
- import java.util.HashMap;
18
- import java.util.List;
19
- import java.util.Map;
20
-
21
- @RestController
22
- @RequestMapping("/api/categories")
23
- @Validated
24
- @CrossOrigin(origins = "*", maxAge = 3600)
25
- public class CategoryController {
26
-
27
- private static final Logger logger = LoggerFactory.getLogger(CategoryController.class);
28
-
29
- private final CategoryService categoryService;
30
-
31
- public CategoryController(CategoryService categoryService) {
32
- this.categoryService = categoryService;
33
- }
34
-
35
- @PostMapping
36
- public ResponseEntity<?> createCategory(@Valid @RequestBody CreateCategoryRequest request) {
37
- logger.info("Request to create category: {}", request.getName());
38
-
39
- try {
40
- CategoryDto createdCategory = categoryService.createCategory(request);
41
-
42
- Map<String, Object> response = new HashMap<>();
43
- response.put("success", true);
44
- response.put("message", "Category created successfully");
45
- response.put("data", createdCategory);
46
-
47
- return ResponseEntity.status(HttpStatus.CREATED).body(response);
48
-
49
- } catch (IllegalArgumentException e) {
50
- logger.error("Invalid category creation request: {}", e.getMessage());
51
-
52
- Map<String, Object> errorResponse = new HashMap<>();
53
- errorResponse.put("success", false);
54
- errorResponse.put("message", e.getMessage());
55
- errorResponse.put("error_code", "CATEGORY_ALREADY_EXISTS");
56
-
57
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
58
-
59
- } catch (CategoryNotFoundException e) {
60
- logger.error("Parent category not found: {}", e.getMessage());
61
-
62
- Map<String, Object> errorResponse = new HashMap<>();
63
- errorResponse.put("success", false);
64
- errorResponse.put("message", "Parent category not found");
65
- errorResponse.put("error_code", "PARENT_CATEGORY_NOT_FOUND");
66
-
67
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
68
- }
69
- }
70
-
71
- @GetMapping
72
- public ResponseEntity<?> getAllCategories(
73
- @RequestParam(value = "sort", defaultValue = "name") String sortBy) {
74
- logger.info("Request to get all categories with sort: {}", sortBy);
75
-
76
- try {
77
- List<CategoryDto> categories = categoryService.getAllCategories(sortBy);
78
-
79
- Map<String, Object> response = new HashMap<>();
80
- response.put("success", true);
81
- response.put("message", "Categories retrieved successfully");
82
- response.put("data", categories);
83
- response.put("count", categories.size());
84
-
85
- return ResponseEntity.ok(response);
86
-
87
- } catch (Exception e) {
88
- logger.error("Error retrieving categories: {}", e.getMessage());
89
-
90
- Map<String, Object> errorResponse = new HashMap<>();
91
- errorResponse.put("success", false);
92
- errorResponse.put("message", "Error retrieving categories");
93
- errorResponse.put("error_code", "INTERNAL_ERROR");
94
-
95
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
96
- }
97
- }
98
-
99
- @GetMapping("/{categoryId}")
100
- public ResponseEntity<?> getCategoryById(
101
- @PathVariable @NotBlank(message = "Category ID is required") String categoryId) {
102
- logger.info("Request to get category by ID: {}", categoryId);
103
-
104
- try {
105
- CategoryDto category = categoryService.getCategoryById(categoryId);
106
-
107
- Map<String, Object> response = new HashMap<>();
108
- response.put("success", true);
109
- response.put("message", "Category retrieved successfully");
110
- response.put("data", category);
111
-
112
- return ResponseEntity.ok(response);
113
-
114
- } catch (CategoryNotFoundException e) {
115
- logger.error("Category not found: {}", e.getMessage());
116
-
117
- Map<String, Object> errorResponse = new HashMap<>();
118
- errorResponse.put("success", false);
119
- errorResponse.put("message", e.getMessage());
120
- errorResponse.put("error_code", "CATEGORY_NOT_FOUND");
121
-
122
- return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse);
123
- }
124
- }
125
-
126
- @GetMapping("/root")
127
- public ResponseEntity<?> getRootCategories(
128
- @RequestParam(value = "sort", defaultValue = "name") String sortBy) {
129
- logger.info("Request to get root categories with sort: {}", sortBy);
130
-
131
- try {
132
- List<CategoryDto> rootCategories = categoryService.getRootCategories(sortBy);
133
-
134
- Map<String, Object> response = new HashMap<>();
135
- response.put("success", true);
136
- response.put("message", "Root categories retrieved successfully");
137
- response.put("data", rootCategories);
138
- response.put("count", rootCategories.size());
139
-
140
- return ResponseEntity.ok(response);
141
-
142
- } catch (Exception e) {
143
- logger.error("Error retrieving root categories: {}", e.getMessage());
144
-
145
- Map<String, Object> errorResponse = new HashMap<>();
146
- errorResponse.put("success", false);
147
- errorResponse.put("message", "Error retrieving root categories");
148
- errorResponse.put("error_code", "INTERNAL_ERROR");
149
-
150
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
151
- }
152
- }
153
-
154
- @GetMapping("/{parentId}/subcategories")
155
- public ResponseEntity<?> getSubcategories(
156
- @PathVariable @NotBlank(message = "Parent category ID is required") String parentId,
157
- @RequestParam(value = "sort", defaultValue = "name") String sortBy) {
158
- logger.info("Request to get subcategories for parent: {}", parentId);
159
-
160
- try {
161
- List<CategoryDto> subcategories = categoryService.getSubcategories(parentId, sortBy);
162
-
163
- Map<String, Object> response = new HashMap<>();
164
- response.put("success", true);
165
- response.put("message", "Subcategories retrieved successfully");
166
- response.put("data", subcategories);
167
- response.put("count", subcategories.size());
168
- response.put("parent_id", parentId);
169
-
170
- return ResponseEntity.ok(response);
171
-
172
- } catch (CategoryNotFoundException e) {
173
- logger.error("Parent category not found: {}", e.getMessage());
174
-
175
- Map<String, Object> errorResponse = new HashMap<>();
176
- errorResponse.put("success", false);
177
- errorResponse.put("message", e.getMessage());
178
- errorResponse.put("error_code", "PARENT_CATEGORY_NOT_FOUND");
179
-
180
- return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse);
181
- }
182
- }
183
-
184
- @GetMapping("/search")
185
- public ResponseEntity<?> searchCategories(
186
- @RequestParam @NotBlank(message = "Search term is required") String q,
187
- @RequestParam(value = "sort", defaultValue = "name") String sortBy) {
188
- logger.info("Request to search categories with term: {}", q);
189
-
190
- try {
191
- List<CategoryDto> categories = categoryService.searchCategories(q, sortBy);
192
-
193
- Map<String, Object> response = new HashMap<>();
194
- response.put("success", true);
195
- response.put("message", "Categories search completed successfully");
196
- response.put("data", categories);
197
- response.put("count", categories.size());
198
- response.put("search_term", q);
199
-
200
- return ResponseEntity.ok(response);
201
-
202
- } catch (Exception e) {
203
- logger.error("Error searching categories: {}", e.getMessage());
204
-
205
- Map<String, Object> errorResponse = new HashMap<>();
206
- errorResponse.put("success", false);
207
- errorResponse.put("message", "Error searching categories");
208
- errorResponse.put("error_code", "SEARCH_ERROR");
209
-
210
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
211
- }
212
- }
213
-
214
- @GetMapping("/popular")
215
- public ResponseEntity<?> getPopularCategories(
216
- @RequestParam(value = "min_items", defaultValue = "1") long minItems) {
217
- logger.info("Request to get popular categories with minimum {} items", minItems);
218
-
219
- try {
220
- List<CategoryDto> popularCategories = categoryService.getPopularCategories(minItems);
221
-
222
- Map<String, Object> response = new HashMap<>();
223
- response.put("success", true);
224
- response.put("message", "Popular categories retrieved successfully");
225
- response.put("data", popularCategories);
226
- response.put("count", popularCategories.size());
227
- response.put("min_items", minItems);
228
-
229
- return ResponseEntity.ok(response);
230
-
231
- } catch (Exception e) {
232
- logger.error("Error retrieving popular categories: {}", e.getMessage());
233
-
234
- Map<String, Object> errorResponse = new HashMap<>();
235
- errorResponse.put("success", false);
236
- errorResponse.put("message", "Error retrieving popular categories");
237
- errorResponse.put("error_code", "INTERNAL_ERROR");
238
-
239
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
240
- }
241
- }
242
-
243
- @PatchMapping("/{categoryId}/status")
244
- public ResponseEntity<?> updateCategoryStatus(
245
- @PathVariable @NotBlank(message = "Category ID is required") String categoryId,
246
- @RequestParam boolean is_active) {
247
- logger.info("Request to update category {} status to: {}", categoryId, is_active);
248
-
249
- try {
250
- CategoryDto updatedCategory = categoryService.updateCategoryStatus(categoryId, is_active);
251
-
252
- Map<String, Object> response = new HashMap<>();
253
- response.put("success", true);
254
- response.put("message", "Category status updated successfully");
255
- response.put("data", updatedCategory);
256
-
257
- return ResponseEntity.ok(response);
258
-
259
- } catch (CategoryNotFoundException e) {
260
- logger.error("Category not found: {}", e.getMessage());
261
-
262
- Map<String, Object> errorResponse = new HashMap<>();
263
- errorResponse.put("success", false);
264
- errorResponse.put("message", e.getMessage());
265
- errorResponse.put("error_code", "CATEGORY_NOT_FOUND");
266
-
267
- return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse);
268
- }
269
- }
270
-
271
- @ExceptionHandler(org.springframework.web.bind.MethodArgumentNotValidException.class)
272
- public ResponseEntity<?> handleValidationExceptions(
273
- org.springframework.web.bind.MethodArgumentNotValidException ex) {
274
-
275
- Map<String, String> errors = new HashMap<>();
276
- ex.getBindingResult().getFieldErrors().forEach(error ->
277
- errors.put(error.getField(), error.getDefaultMessage()));
278
-
279
- Map<String, Object> errorResponse = new HashMap<>();
280
- errorResponse.put("success", false);
281
- errorResponse.put("message", "Validation failed");
282
- errorResponse.put("error_code", "VALIDATION_ERROR");
283
- errorResponse.put("errors", errors);
284
-
285
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
286
- }
287
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/controller/ImageController.java DELETED
@@ -1,200 +0,0 @@
1
- package com.locallend.locallend.controller;
2
-
3
- import com.locallend.locallend.service.ImageService;
4
- import org.slf4j.Logger;
5
- import org.slf4j.LoggerFactory;
6
- import org.springframework.http.HttpStatus;
7
- import org.springframework.http.ResponseEntity;
8
- import org.springframework.web.bind.annotation.*;
9
- import org.springframework.web.multipart.MultipartFile;
10
-
11
- import java.util.HashMap;
12
- import java.util.List;
13
- import java.util.Map;
14
-
15
- /**
16
- * REST Controller for image upload operations.
17
- * Handles file uploads to Cloudinary and image deletion.
18
- */
19
- @RestController
20
- @RequestMapping("/api/images")
21
- @CrossOrigin(origins = "*", maxAge = 3600)
22
- public class ImageController {
23
-
24
- private static final Logger logger = LoggerFactory.getLogger(ImageController.class);
25
-
26
- private final ImageService imageService;
27
-
28
- public ImageController(ImageService imageService) {
29
- this.imageService = imageService;
30
- }
31
-
32
- /**
33
- * Upload a single image
34
- * POST /api/images/upload
35
- * @param file The image file to upload
36
- * @param folder Optional folder name (defaults to "locallend/items")
37
- * @return JSON response with image URL
38
- */
39
- @PostMapping("/upload")
40
- public ResponseEntity<?> uploadImage(
41
- @RequestParam("file") MultipartFile file,
42
- @RequestParam(value = "folder", defaultValue = "locallend/items") String folder) {
43
-
44
- try {
45
- logger.info("Received image upload request: {}", file.getOriginalFilename());
46
-
47
- if (!imageService.isConfigured()) {
48
- Map<String, String> error = new HashMap<>();
49
- error.put("error", "Cloudinary not configured");
50
- error.put("message", "Please configure Cloudinary credentials in application.properties");
51
- return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(error);
52
- }
53
-
54
- String imageUrl = imageService.uploadImage(file, folder);
55
-
56
- Map<String, Object> response = new HashMap<>();
57
- response.put("success", true);
58
- response.put("message", "Image uploaded successfully");
59
- response.put("url", imageUrl);
60
- response.put("filename", file.getOriginalFilename());
61
-
62
- return ResponseEntity.ok(response);
63
-
64
- } catch (IllegalArgumentException e) {
65
- logger.warn("Image validation failed: {}", e.getMessage());
66
- Map<String, String> error = new HashMap<>();
67
- error.put("error", "Validation failed");
68
- error.put("message", e.getMessage());
69
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
70
-
71
- } catch (Exception e) {
72
- logger.error("Image upload failed", e);
73
- Map<String, String> error = new HashMap<>();
74
- error.put("error", "Upload failed");
75
- error.put("message", "Failed to upload image: " + e.getMessage());
76
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
77
- }
78
- }
79
-
80
- /**
81
- * Upload multiple images
82
- * POST /api/images/upload-multiple
83
- * @param files Array of image files to upload
84
- * @param folder Optional folder name (defaults to "locallend/items")
85
- * @return JSON response with array of image URLs
86
- */
87
- @PostMapping("/upload-multiple")
88
- public ResponseEntity<?> uploadMultipleImages(
89
- @RequestParam("files") MultipartFile[] files,
90
- @RequestParam(value = "folder", defaultValue = "locallend/items") String folder) {
91
-
92
- try {
93
- logger.info("Received multiple image upload request: {} files", files.length);
94
-
95
- if (!imageService.isConfigured()) {
96
- Map<String, String> error = new HashMap<>();
97
- error.put("error", "Cloudinary not configured");
98
- error.put("message", "Please configure Cloudinary credentials in application.properties");
99
- return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(error);
100
- }
101
-
102
- if (files.length == 0) {
103
- Map<String, String> error = new HashMap<>();
104
- error.put("error", "No files provided");
105
- error.put("message", "Please select at least one image to upload");
106
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
107
- }
108
-
109
- if (files.length > 10) {
110
- Map<String, String> error = new HashMap<>();
111
- error.put("error", "Too many files");
112
- error.put("message", "Maximum 10 images can be uploaded at once");
113
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
114
- }
115
-
116
- List<String> imageUrls = imageService.uploadMultipleImages(files, folder);
117
-
118
- Map<String, Object> response = new HashMap<>();
119
- response.put("success", true);
120
- response.put("message", "Images uploaded successfully");
121
- response.put("urls", imageUrls);
122
- response.put("count", imageUrls.size());
123
-
124
- return ResponseEntity.ok(response);
125
-
126
- } catch (IllegalArgumentException e) {
127
- logger.warn("Image validation failed: {}", e.getMessage());
128
- Map<String, String> error = new HashMap<>();
129
- error.put("error", "Validation failed");
130
- error.put("message", e.getMessage());
131
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
132
-
133
- } catch (Exception e) {
134
- logger.error("Multiple image upload failed", e);
135
- Map<String, String> error = new HashMap<>();
136
- error.put("error", "Upload failed");
137
- error.put("message", "Failed to upload images: " + e.getMessage());
138
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
139
- }
140
- }
141
-
142
- /**
143
- * Delete an image from Cloudinary
144
- * DELETE /api/images
145
- * @param imageUrl The full Cloudinary URL of the image to delete
146
- * @return Success or error response
147
- */
148
- @DeleteMapping
149
- public ResponseEntity<?> deleteImage(@RequestParam("url") String imageUrl) {
150
- try {
151
- logger.info("Received image deletion request: {}", imageUrl);
152
-
153
- if (!imageService.isConfigured()) {
154
- Map<String, String> error = new HashMap<>();
155
- error.put("error", "Cloudinary not configured");
156
- error.put("message", "Please configure Cloudinary credentials in application.properties");
157
- return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(error);
158
- }
159
-
160
- boolean deleted = imageService.deleteImage(imageUrl);
161
-
162
- if (deleted) {
163
- Map<String, Object> response = new HashMap<>();
164
- response.put("success", true);
165
- response.put("message", "Image deleted successfully");
166
- return ResponseEntity.ok(response);
167
- } else {
168
- Map<String, String> error = new HashMap<>();
169
- error.put("error", "Deletion failed");
170
- error.put("message", "Could not delete image. It may not exist or URL is invalid.");
171
- return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
172
- }
173
-
174
- } catch (Exception e) {
175
- logger.error("Image deletion failed", e);
176
- Map<String, String> error = new HashMap<>();
177
- error.put("error", "Deletion failed");
178
- error.put("message", "Failed to delete image: " + e.getMessage());
179
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
180
- }
181
- }
182
-
183
- /**
184
- * Health check endpoint to verify Cloudinary configuration
185
- * GET /api/images/health
186
- * @return Configuration status
187
- */
188
- @GetMapping("/health")
189
- public ResponseEntity<?> healthCheck() {
190
- Map<String, Object> response = new HashMap<>();
191
- boolean configured = imageService.isConfigured();
192
-
193
- response.put("configured", configured);
194
- response.put("message", configured
195
- ? "Cloudinary is properly configured"
196
- : "Cloudinary credentials not configured");
197
-
198
- return ResponseEntity.ok(response);
199
- }
200
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/controller/ItemController.java DELETED
@@ -1,349 +0,0 @@
1
- package com.locallend.locallend.controller;
2
-
3
- import com.locallend.locallend.dto.CreateItemRequest;
4
- import com.locallend.locallend.dto.ItemDTO;
5
- import com.locallend.locallend.dto.UpdateItemRequest;
6
- import com.locallend.locallend.service.ItemService;
7
- import jakarta.validation.Valid;
8
- import org.springframework.data.domain.Page;
9
- import org.springframework.http.HttpStatus;
10
- import org.springframework.http.ResponseEntity;
11
- import org.springframework.web.bind.annotation.*;
12
-
13
- import java.util.HashMap;
14
- import java.util.Map;
15
-
16
- /**
17
- * REST Controller for Item management in the LocalLend platform.
18
- * Provides endpoints for CRUD operations, search, filtering, and availability management.
19
- */
20
- @RestController
21
- @RequestMapping("/api/items")
22
- @CrossOrigin(origins = "*", maxAge = 3600)
23
- public class ItemController {
24
-
25
- private final ItemService itemService;
26
-
27
- public ItemController(ItemService itemService) {
28
- this.itemService = itemService;
29
- }
30
-
31
- /**
32
- * Create a new item
33
- * POST /api/items
34
- * Request body: CreateItemRequest (name, description, categoryId, deposit, images, condition)
35
- * Request header: X-User-Id (required for owner identification)
36
- */
37
- @PostMapping
38
- public ResponseEntity<?> createItem(
39
- @Valid @RequestBody CreateItemRequest request,
40
- @RequestHeader(value = "X-User-Id", required = true) String userId) {
41
-
42
- try {
43
- ItemDTO item = itemService.createItem(request, userId);
44
- return ResponseEntity.status(HttpStatus.CREATED).body(item);
45
- } catch (IllegalArgumentException e) {
46
- Map<String, String> error = new HashMap<>();
47
- error.put("error", "Invalid request");
48
- error.put("message", e.getMessage());
49
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
50
- } catch (Exception e) {
51
- Map<String, String> error = new HashMap<>();
52
- error.put("error", "Failed to create item");
53
- error.put("message", e.getMessage());
54
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
55
- }
56
- }
57
-
58
- /**
59
- * Get all available items with pagination and sorting
60
- * GET /api/items
61
- * Query params: page (default: 0), size (default: 10), sortBy (default: name), sortDir (default: asc)
62
- */
63
- @GetMapping
64
- public ResponseEntity<Page<ItemDTO>> getAllAvailableItems(
65
- @RequestParam(defaultValue = "0") int page,
66
- @RequestParam(defaultValue = "10") int size,
67
- @RequestParam(defaultValue = "name") String sortBy,
68
- @RequestParam(defaultValue = "asc") String sortDir,
69
- @RequestParam(required = false) String ownerId) {
70
-
71
- // If caller provided an ownerId, route to the owner-specific service
72
- // This keeps backward compatibility for callers using the public
73
- // /api/items endpoint with ?ownerId=<id>
74
- if (ownerId != null && !ownerId.isBlank()) {
75
- Page<ItemDTO> items = itemService.getItemsByOwner(ownerId, page, size);
76
- return ResponseEntity.ok(items);
77
- }
78
-
79
- Page<ItemDTO> items = itemService.getAvailableItems(page, size, sortBy, sortDir);
80
- return ResponseEntity.ok(items);
81
- }
82
-
83
- /**
84
- * Get item by ID
85
- * GET /api/items/{id}
86
- */
87
- @GetMapping("/{id}")
88
- public ResponseEntity<?> getItemById(@PathVariable String id) {
89
- try {
90
- ItemDTO item = itemService.getItemById(id);
91
- return ResponseEntity.ok(item);
92
- } catch (Exception e) {
93
- Map<String, String> error = new HashMap<>();
94
- error.put("error", "Item not found");
95
- error.put("message", e.getMessage());
96
- return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
97
- }
98
- }
99
-
100
- /**
101
- * Search available items
102
- * GET /api/items/search
103
- * Query params: q (search term), page (default: 0), size (default: 10)
104
- */
105
- @GetMapping("/search")
106
- public ResponseEntity<Page<ItemDTO>> searchItems(
107
- @RequestParam(required = true) String q,
108
- @RequestParam(defaultValue = "0") int page,
109
- @RequestParam(defaultValue = "10") int size) {
110
-
111
- Page<ItemDTO> items = itemService.searchAvailableItems(q, page, size);
112
- return ResponseEntity.ok(items);
113
- }
114
-
115
- /**
116
- * Get items by category
117
- * GET /api/items/category/{categoryId}
118
- * Query params: page (default: 0), size (default: 10)
119
- */
120
- @GetMapping("/category/{categoryId}")
121
- public ResponseEntity<?> getItemsByCategory(
122
- @PathVariable String categoryId,
123
- @RequestParam(defaultValue = "0") int page,
124
- @RequestParam(defaultValue = "10") int size) {
125
-
126
- try {
127
- Page<ItemDTO> items = itemService.getAvailableItemsByCategory(categoryId, page, size);
128
- return ResponseEntity.ok(items);
129
- } catch (IllegalArgumentException e) {
130
- Map<String, String> error = new HashMap<>();
131
- error.put("error", "Invalid category");
132
- error.put("message", e.getMessage());
133
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
134
- }
135
- }
136
-
137
- /**
138
- * Get items by owner
139
- * GET /api/items/owner/{ownerId}
140
- * Query params: page (default: 0), size (default: 10)
141
- */
142
- @GetMapping("/owner/{ownerId}")
143
- public ResponseEntity<?> getItemsByOwner(
144
- @PathVariable String ownerId,
145
- @RequestParam(defaultValue = "0") int page,
146
- @RequestParam(defaultValue = "10") int size) {
147
-
148
- try {
149
- Page<ItemDTO> items = itemService.getItemsByOwner(ownerId, page, size);
150
- return ResponseEntity.ok(items);
151
- } catch (IllegalArgumentException e) {
152
- Map<String, String> error = new HashMap<>();
153
- error.put("error", "Invalid owner");
154
- error.put("message", e.getMessage());
155
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
156
- }
157
- }
158
-
159
- /**
160
- * Get current user's items
161
- * GET /api/items/my-items
162
- * Request header: X-User-Id (required for authentication)
163
- */
164
- @GetMapping("/my-items")
165
- public ResponseEntity<?> getMyItems(
166
- @RequestParam(defaultValue = "0") int page,
167
- @RequestParam(defaultValue = "10") int size,
168
- @RequestHeader(value = "X-User-Id", required = true) String userId) {
169
-
170
- try {
171
- Page<ItemDTO> items = itemService.getItemsByOwner(userId, page, size);
172
- return ResponseEntity.ok(items);
173
- } catch (Exception e) {
174
- Map<String, String> error = new HashMap<>();
175
- error.put("error", "Failed to retrieve items");
176
- error.put("message", e.getMessage());
177
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
178
- }
179
- }
180
-
181
- /**
182
- * Update an item
183
- * PUT /api/items/{id}
184
- * Request body: UpdateItemRequest (optional fields: name, description, condition, deposit, images, isAvailable)
185
- * Request header: X-User-Id (required for owner verification)
186
- */
187
- @PutMapping("/{id}")
188
- public ResponseEntity<?> updateItem(
189
- @PathVariable String id,
190
- @Valid @RequestBody UpdateItemRequest request,
191
- @RequestHeader(value = "X-User-Id", required = true) String userId) {
192
-
193
- try {
194
- ItemDTO updatedItem = itemService.updateItem(id, request, userId);
195
- return ResponseEntity.ok(updatedItem);
196
- } catch (com.locallend.locallend.exception.UnauthorizedItemAccessException e) {
197
- Map<String, String> error = new HashMap<>();
198
- error.put("error", "Access denied");
199
- error.put("message", e.getMessage());
200
- return ResponseEntity.status(HttpStatus.FORBIDDEN).body(error);
201
- } catch (com.locallend.locallend.exception.ItemNotFoundException e) {
202
- Map<String, String> error = new HashMap<>();
203
- error.put("error", "Item not found");
204
- error.put("message", e.getMessage());
205
- return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
206
- } catch (Exception e) {
207
- Map<String, String> error = new HashMap<>();
208
- error.put("error", "Failed to update item");
209
- error.put("message", e.getMessage());
210
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
211
- }
212
- }
213
-
214
- /**
215
- * Toggle item availability
216
- * PATCH /api/items/{id}/toggle-availability
217
- * Request header: X-User-Id (required for owner verification)
218
- */
219
- @PatchMapping("/{id}/toggle-availability")
220
- public ResponseEntity<?> toggleAvailability(
221
- @PathVariable String id,
222
- @RequestHeader(value = "X-User-Id", required = true) String userId) {
223
-
224
- try {
225
- ItemDTO updatedItem = itemService.toggleAvailability(id, userId);
226
- return ResponseEntity.ok(updatedItem);
227
- } catch (com.locallend.locallend.exception.UnauthorizedItemAccessException e) {
228
- Map<String, String> error = new HashMap<>();
229
- error.put("error", "Access denied");
230
- error.put("message", e.getMessage());
231
- return ResponseEntity.status(HttpStatus.FORBIDDEN).body(error);
232
- } catch (com.locallend.locallend.exception.ItemNotFoundException e) {
233
- Map<String, String> error = new HashMap<>();
234
- error.put("error", "Item not found");
235
- error.put("message", e.getMessage());
236
- return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
237
- } catch (Exception e) {
238
- Map<String, String> error = new HashMap<>();
239
- error.put("error", "Failed to toggle availability");
240
- error.put("message", e.getMessage());
241
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
242
- }
243
- }
244
-
245
- /**
246
- * Set item availability
247
- * PATCH /api/items/{id}/availability
248
- * Request body: { "isAvailable": true/false }
249
- * Request header: X-User-Id (required for owner verification)
250
- */
251
- @PatchMapping("/{id}/availability")
252
- public ResponseEntity<?> setAvailability(
253
- @PathVariable String id,
254
- @RequestBody Map<String, Boolean> request,
255
- @RequestHeader(value = "X-User-Id", required = true) String userId) {
256
-
257
- try {
258
- Boolean isAvailable = request.get("isAvailable");
259
- if (isAvailable == null) {
260
- Map<String, String> error = new HashMap<>();
261
- error.put("error", "Invalid request");
262
- error.put("message", "isAvailable field is required");
263
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
264
- }
265
-
266
- ItemDTO updatedItem = itemService.setAvailability(id, isAvailable, userId);
267
- return ResponseEntity.ok(updatedItem);
268
- } catch (com.locallend.locallend.exception.UnauthorizedItemAccessException e) {
269
- Map<String, String> error = new HashMap<>();
270
- error.put("error", "Access denied");
271
- error.put("message", e.getMessage());
272
- return ResponseEntity.status(HttpStatus.FORBIDDEN).body(error);
273
- } catch (com.locallend.locallend.exception.ItemNotFoundException e) {
274
- Map<String, String> error = new HashMap<>();
275
- error.put("error", "Item not found");
276
- error.put("message", e.getMessage());
277
- return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
278
- } catch (Exception e) {
279
- Map<String, String> error = new HashMap<>();
280
- error.put("error", "Failed to set availability");
281
- error.put("message", e.getMessage());
282
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
283
- }
284
- }
285
-
286
- /**
287
- * Delete an item (soft delete)
288
- * DELETE /api/items/{id}
289
- * Request header: X-User-Id (required for owner verification)
290
- */
291
- @DeleteMapping("/{id}")
292
- public ResponseEntity<?> deleteItem(
293
- @PathVariable String id,
294
- @RequestHeader(value = "X-User-Id", required = true) String userId) {
295
-
296
- try {
297
- itemService.deleteItem(id, userId);
298
- Map<String, String> response = new HashMap<>();
299
- response.put("message", "Item deleted successfully");
300
- return ResponseEntity.ok(response);
301
- } catch (com.locallend.locallend.exception.UnauthorizedItemAccessException e) {
302
- Map<String, String> error = new HashMap<>();
303
- error.put("error", "Access denied");
304
- error.put("message", e.getMessage());
305
- return ResponseEntity.status(HttpStatus.FORBIDDEN).body(error);
306
- } catch (com.locallend.locallend.exception.ItemNotFoundException e) {
307
- Map<String, String> error = new HashMap<>();
308
- error.put("error", "Item not found");
309
- error.put("message", e.getMessage());
310
- return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
311
- } catch (Exception e) {
312
- Map<String, String> error = new HashMap<>();
313
- error.put("error", "Failed to delete item");
314
- error.put("message", e.getMessage());
315
- return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
316
- }
317
- }
318
-
319
- /**
320
- * Reactivate a soft-deleted item (for testing purposes)
321
- * POST /api/items/{id}/reactivate
322
- * Request header: X-User-Id (required for owner verification)
323
- */
324
- @PostMapping("/{id}/reactivate")
325
- public ResponseEntity<?> reactivateItem(
326
- @PathVariable String id,
327
- @RequestHeader(value = "X-User-Id", required = true) String userId) {
328
-
329
- try {
330
- ItemDTO item = itemService.reactivateItem(id, userId);
331
- return ResponseEntity.ok(item);
332
- } catch (com.locallend.locallend.exception.UnauthorizedItemAccessException e) {
333
- Map<String, String> error = new HashMap<>();
334
- error.put("error", "Access denied");
335
- error.put("message", e.getMessage());
336
- return ResponseEntity.status(HttpStatus.FORBIDDEN).body(error);
337
- } catch (com.locallend.locallend.exception.ItemNotFoundException e) {
338
- Map<String, String> error = new HashMap<>();
339
- error.put("error", "Item not found");
340
- error.put("message", e.getMessage());
341
- return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
342
- } catch (Exception e) {
343
- Map<String, String> error = new HashMap<>();
344
- error.put("error", "Failed to reactivate item");
345
- error.put("message", e.getMessage());
346
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
347
- }
348
- }
349
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/controller/RatingController.java DELETED
@@ -1,531 +0,0 @@
1
- package com.locallend.locallend.controller;
2
-
3
- import com.locallend.locallend.dto.request.RatingCreateDto;
4
- import com.locallend.locallend.dto.response.RatingResponseDto;
5
- import com.locallend.locallend.dto.response.RatingStatsResponse;
6
- import com.locallend.locallend.exception.ValidationException;
7
- import com.locallend.locallend.model.Rating;
8
- import com.locallend.locallend.model.RatingType;
9
- import com.locallend.locallend.service.RatingService;
10
- import com.locallend.locallend.util.RatingMapper;
11
- import com.locallend.locallend.util.SecurityUtils;
12
-
13
- import org.slf4j.Logger;
14
- import org.slf4j.LoggerFactory;
15
- import org.springframework.http.HttpStatus;
16
- import org.springframework.http.ResponseEntity;
17
- import org.springframework.validation.annotation.Validated;
18
- import org.springframework.web.bind.annotation.*;
19
-
20
- import jakarta.validation.Valid;
21
- import jakarta.validation.constraints.Min;
22
- import jakarta.validation.constraints.NotBlank;
23
- import java.util.HashMap;
24
- import java.util.List;
25
- import java.util.Map;
26
-
27
- /**
28
- * REST Controller for Rating management operations.
29
- * Provides comprehensive API endpoints for creating and retrieving ratings.
30
- * Issue #25: Rating Controller APIs
31
- */
32
- @RestController
33
- @RequestMapping("/api/ratings")
34
- @Validated
35
- @CrossOrigin(origins = "*", maxAge = 3600)
36
- public class RatingController {
37
-
38
- private static final Logger logger = LoggerFactory.getLogger(RatingController.class);
39
-
40
- private final RatingService ratingService;
41
- private final RatingMapper ratingMapper;
42
-
43
- public RatingController(RatingService ratingService, RatingMapper ratingMapper) {
44
- this.ratingService = ratingService;
45
- this.ratingMapper = ratingMapper;
46
- }
47
-
48
- /**
49
- * Create a new rating
50
- * POST /api/ratings
51
- */
52
- @PostMapping
53
- public ResponseEntity<?> createRating(@Valid @RequestBody RatingCreateDto request) {
54
-
55
- logger.info("Request to create rating with type: {}", request.getRatingType());
56
-
57
- try {
58
- String raterId = SecurityUtils.getCurrentUserId();
59
- Rating rating;
60
-
61
- // Route to appropriate service method based on rating type
62
- if (request.getRatingType() == RatingType.USER_TO_ITEM) {
63
- // Item rating
64
- if (request.getItemId() == null || request.getItemId().trim().isEmpty()) {
65
- throw new ValidationException("Item ID is required for item ratings");
66
- }
67
- rating = ratingService.createItemRating(
68
- raterId,
69
- request.getItemId(),
70
- request.getRatingValue(),
71
- request.getComment(),
72
- request.getIsAnonymous() != null ? request.getIsAnonymous() : false
73
- );
74
- } else {
75
- // User-to-user rating (USER_TO_USER, OWNER_TO_BORROWER, BORROWER_TO_OWNER)
76
- if (request.getRateeId() == null || request.getRateeId().trim().isEmpty()) {
77
- throw new ValidationException("Ratee ID is required for user ratings");
78
- }
79
- rating = ratingService.createUserRating(
80
- raterId,
81
- request.getRateeId(),
82
- request.getRatingType(),
83
- request.getRatingValue(),
84
- request.getComment(),
85
- request.getBookingId()
86
- );
87
- }
88
-
89
- RatingResponseDto responseDto = ratingMapper.toResponseDto(rating);
90
-
91
- Map<String, Object> response = new HashMap<>();
92
- response.put("success", true);
93
- response.put("message", "Rating created successfully");
94
- response.put("data", responseDto);
95
-
96
- return ResponseEntity.status(HttpStatus.CREATED).body(response);
97
-
98
- } catch (ValidationException e) {
99
- logger.error("Rating validation failed: {}", e.getMessage());
100
-
101
- Map<String, Object> errorResponse = new HashMap<>();
102
- errorResponse.put("success", false);
103
- errorResponse.put("message", e.getMessage());
104
- errorResponse.put("error_code", "VALIDATION_ERROR");
105
-
106
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
107
-
108
- } catch (Exception e) {
109
- logger.error("Unexpected error creating rating: {}", e.getMessage());
110
-
111
- Map<String, Object> errorResponse = new HashMap<>();
112
- errorResponse.put("success", false);
113
- errorResponse.put("message", "Internal server error: " + e.getMessage());
114
- errorResponse.put("error_code", "INTERNAL_ERROR");
115
-
116
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
117
- }
118
- }
119
-
120
- /**
121
- * Get ratings for a user
122
- * GET /api/ratings/user/{userId}
123
- */
124
- @GetMapping("/user/{userId}")
125
- public ResponseEntity<?> getUserRatings(
126
- @PathVariable @NotBlank(message = "User ID is required") String userId) {
127
-
128
- logger.info("Request to get ratings for user: {}", userId);
129
-
130
- try {
131
- List<Rating> ratings = ratingService.getRatingsReceivedByUser(userId);
132
- List<RatingResponseDto> responseDtos = ratingMapper.toResponseDtoList(ratings);
133
-
134
- Map<String, Object> response = new HashMap<>();
135
- response.put("success", true);
136
- response.put("message", "User ratings retrieved successfully");
137
- response.put("data", responseDtos);
138
- response.put("count", responseDtos.size());
139
-
140
- return ResponseEntity.ok(response);
141
-
142
- } catch (Exception e) {
143
- logger.error("Error retrieving user ratings: {}", e.getMessage());
144
-
145
- Map<String, Object> errorResponse = new HashMap<>();
146
- errorResponse.put("success", false);
147
- errorResponse.put("message", "Error retrieving user ratings");
148
- errorResponse.put("error_code", "RETRIEVAL_ERROR");
149
-
150
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
151
- }
152
- }
153
-
154
- /**
155
- * Get ratings for an item
156
- * GET /api/ratings/item/{itemId}
157
- */
158
- @GetMapping("/item/{itemId}")
159
- public ResponseEntity<?> getItemRatings(
160
- @PathVariable @NotBlank(message = "Item ID is required") String itemId) {
161
-
162
- logger.info("Request to get ratings for item: {}", itemId);
163
-
164
- try {
165
- List<Rating> ratings = ratingService.getItemRatings(itemId);
166
- List<RatingResponseDto> responseDtos = ratingMapper.toResponseDtoList(ratings);
167
-
168
- Map<String, Object> response = new HashMap<>();
169
- response.put("success", true);
170
- response.put("message", "Item ratings retrieved successfully");
171
- response.put("data", responseDtos);
172
- response.put("count", responseDtos.size());
173
-
174
- return ResponseEntity.ok(response);
175
-
176
- } catch (Exception e) {
177
- logger.error("Error retrieving item ratings: {}", e.getMessage());
178
-
179
- Map<String, Object> errorResponse = new HashMap<>();
180
- errorResponse.put("success", false);
181
- errorResponse.put("message", "Error retrieving item ratings");
182
- errorResponse.put("error_code", "RETRIEVAL_ERROR");
183
-
184
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
185
- }
186
- }
187
-
188
- /**
189
- * Get ratings for a booking
190
- * GET /api/ratings/booking/{bookingId}
191
- */
192
- @GetMapping("/booking/{bookingId}")
193
- public ResponseEntity<?> getBookingRatings(
194
- @PathVariable @NotBlank(message = "Booking ID is required") String bookingId) {
195
-
196
- logger.info("Request to get ratings for booking: {}", bookingId);
197
-
198
- try {
199
- // Filter ratings by booking ID from user and item ratings
200
- // The existing service doesn't have a specific getByBookingId method
201
- // For now, we'll return empty list and log a note
202
- logger.warn("Booking-specific rating retrieval not yet implemented in service layer");
203
-
204
- Map<String, Object> response = new HashMap<>();
205
- response.put("success", true);
206
- response.put("message", "Booking ratings retrieved successfully");
207
- response.put("data", List.of());
208
- response.put("count", 0);
209
- response.put("booking_id", bookingId);
210
- response.put("note", "Booking-specific queries to be added in future iteration");
211
-
212
- return ResponseEntity.ok(response);
213
-
214
- } catch (Exception e) {
215
- logger.error("Error retrieving booking ratings: {}", e.getMessage());
216
-
217
- Map<String, Object> errorResponse = new HashMap<>();
218
- errorResponse.put("success", false);
219
- errorResponse.put("message", "Error retrieving booking ratings");
220
- errorResponse.put("error_code", "RETRIEVAL_ERROR");
221
-
222
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
223
- }
224
- }
225
-
226
- /**
227
- * Get ratings given by current user
228
- * GET /api/ratings/my-ratings
229
- */
230
- @GetMapping("/my-ratings")
231
- public ResponseEntity<?> getMyRatings() {
232
-
233
- String userId = SecurityUtils.getCurrentUserId();
234
- logger.info("Request to get ratings by user: {}", userId);
235
-
236
- try {
237
- List<Rating> ratings = ratingService.getRatingsByRater(userId);
238
- List<RatingResponseDto> responseDtos = ratingMapper.toResponseDtoList(ratings);
239
-
240
- Map<String, Object> response = new HashMap<>();
241
- response.put("success", true);
242
- response.put("message", "My ratings retrieved successfully");
243
- response.put("data", responseDtos);
244
- response.put("count", responseDtos.size());
245
-
246
- return ResponseEntity.ok(response);
247
-
248
- } catch (Exception e) {
249
- logger.error("Error retrieving user's ratings: {}", e.getMessage());
250
-
251
- Map<String, Object> errorResponse = new HashMap<>();
252
- errorResponse.put("success", false);
253
- errorResponse.put("message", "Error retrieving your ratings");
254
- errorResponse.put("error_code", "RETRIEVAL_ERROR");
255
-
256
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
257
- }
258
- }
259
-
260
- /**
261
- * Get rating statistics for a user
262
- * GET /api/ratings/user/{userId}/stats
263
- */
264
- @GetMapping("/user/{userId}/stats")
265
- public ResponseEntity<?> getUserRatingStats(
266
- @PathVariable @NotBlank(message = "User ID is required") String userId) {
267
-
268
- logger.info("Request to get rating stats for user: {}", userId);
269
-
270
- try {
271
- RatingService.RatingStatistics stats = ratingService.getUserRatingStatistics(userId);
272
-
273
- // Convert RatingStatistics to RatingStatsResponse
274
- RatingStatsResponse response = convertToStatsResponse(stats, userId, "USER");
275
-
276
- Map<String, Object> responseMap = new HashMap<>();
277
- responseMap.put("success", true);
278
- responseMap.put("message", "User rating stats retrieved successfully");
279
- responseMap.put("data", response);
280
-
281
- return ResponseEntity.ok(responseMap);
282
-
283
- } catch (Exception e) {
284
- logger.error("Error retrieving user rating stats: {}", e.getMessage());
285
-
286
- Map<String, Object> errorResponse = new HashMap<>();
287
- errorResponse.put("success", false);
288
- errorResponse.put("message", "Error retrieving user rating statistics");
289
- errorResponse.put("error_code", "STATS_ERROR");
290
-
291
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
292
- }
293
- }
294
-
295
- /**
296
- * Get rating statistics for an item
297
- * GET /api/ratings/item/{itemId}/stats
298
- */
299
- @GetMapping("/item/{itemId}/stats")
300
- public ResponseEntity<?> getItemRatingStats(
301
- @PathVariable @NotBlank(message = "Item ID is required") String itemId) {
302
-
303
- logger.info("Request to get rating stats for item: {}", itemId);
304
-
305
- try {
306
- RatingService.RatingStatistics stats = ratingService.getItemRatingStatistics(itemId);
307
-
308
- // Convert RatingStatistics to RatingStatsResponse
309
- RatingStatsResponse response = convertToStatsResponse(stats, itemId, "ITEM");
310
-
311
- Map<String, Object> responseMap = new HashMap<>();
312
- responseMap.put("success", true);
313
- responseMap.put("message", "Item rating stats retrieved successfully");
314
- responseMap.put("data", response);
315
-
316
- return ResponseEntity.ok(responseMap);
317
-
318
- } catch (Exception e) {
319
- logger.error("Error retrieving item rating stats: {}", e.getMessage());
320
-
321
- Map<String, Object> errorResponse = new HashMap<>();
322
- errorResponse.put("success", false);
323
- errorResponse.put("message", "Error retrieving item rating statistics");
324
- errorResponse.put("error_code", "STATS_ERROR");
325
-
326
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
327
- }
328
- }
329
-
330
- /**
331
- * Update an existing rating
332
- * PUT /api/ratings/{ratingId}
333
- */
334
- @PutMapping("/{ratingId}")
335
- public ResponseEntity<?> updateRating(
336
- @PathVariable @NotBlank(message = "Rating ID is required") String ratingId,
337
- @RequestParam @Min(value = 1, message = "Rating must be between 1 and 5") @Min(value = 5) Integer ratingValue,
338
- @RequestParam(required = false) String comment) {
339
-
340
- String userId = SecurityUtils.getCurrentUserId();
341
- logger.info("Request to update rating {} by user: {}", ratingId, userId);
342
-
343
- try {
344
- Rating updatedRating = ratingService.updateRating(ratingId, userId, ratingValue, comment);
345
- RatingResponseDto responseDto = ratingMapper.toResponseDto(updatedRating);
346
-
347
- Map<String, Object> response = new HashMap<>();
348
- response.put("success", true);
349
- response.put("message", "Rating updated successfully");
350
- response.put("data", responseDto);
351
-
352
- return ResponseEntity.ok(response);
353
-
354
- } catch (ValidationException e) {
355
- logger.error("Rating update failed: {}", e.getMessage());
356
-
357
- Map<String, Object> errorResponse = new HashMap<>();
358
- errorResponse.put("success", false);
359
- errorResponse.put("message", e.getMessage());
360
- errorResponse.put("error_code", "VALIDATION_ERROR");
361
-
362
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
363
-
364
- } catch (Exception e) {
365
- logger.error("Error updating rating: {}", e.getMessage());
366
-
367
- Map<String, Object> errorResponse = new HashMap<>();
368
- errorResponse.put("success", false);
369
- errorResponse.put("message", "Error updating rating: " + e.getMessage());
370
- errorResponse.put("error_code", "UPDATE_ERROR");
371
-
372
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
373
- }
374
- }
375
-
376
- /**
377
- * Delete a rating
378
- * DELETE /api/ratings/{ratingId}
379
- */
380
- @DeleteMapping("/{ratingId}")
381
- public ResponseEntity<?> deleteRating(
382
- @PathVariable @NotBlank(message = "Rating ID is required") String ratingId) {
383
-
384
- String userId = SecurityUtils.getCurrentUserId();
385
- logger.info("Request to delete rating {} by user: {}", ratingId, userId);
386
-
387
- try {
388
- ratingService.deleteRating(ratingId, userId);
389
-
390
- Map<String, Object> response = new HashMap<>();
391
- response.put("success", true);
392
- response.put("message", "Rating deleted successfully");
393
-
394
- return ResponseEntity.ok(response);
395
-
396
- } catch (ValidationException e) {
397
- logger.error("Rating deletion failed: {}", e.getMessage());
398
-
399
- Map<String, Object> errorResponse = new HashMap<>();
400
- errorResponse.put("success", false);
401
- errorResponse.put("message", e.getMessage());
402
- errorResponse.put("error_code", "VALIDATION_ERROR");
403
-
404
- return ResponseEntity.status(HttpStatus.FORBIDDEN).body(errorResponse);
405
-
406
- } catch (Exception e) {
407
- logger.error("Error deleting rating: {}", e.getMessage());
408
-
409
- Map<String, Object> errorResponse = new HashMap<>();
410
- errorResponse.put("success", false);
411
- errorResponse.put("message", "Error deleting rating: " + e.getMessage());
412
- errorResponse.put("error_code", "DELETE_ERROR");
413
-
414
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
415
- }
416
- }
417
-
418
- /**
419
- * Get a specific rating by ID
420
- * GET /api/ratings/{ratingId}
421
- */
422
- @GetMapping("/{ratingId}")
423
- public ResponseEntity<?> getRatingById(
424
- @PathVariable @NotBlank(message = "Rating ID is required") String ratingId) {
425
-
426
- logger.info("Request to get rating: {}", ratingId);
427
-
428
- try {
429
- Rating rating = ratingService.getRatingById(ratingId);
430
- RatingResponseDto responseDto = ratingMapper.toResponseDto(rating);
431
-
432
- Map<String, Object> response = new HashMap<>();
433
- response.put("success", true);
434
- response.put("message", "Rating retrieved successfully");
435
- response.put("data", responseDto);
436
-
437
- return ResponseEntity.ok(response);
438
-
439
- } catch (Exception e) {
440
- logger.error("Error retrieving rating: {}", e.getMessage());
441
-
442
- Map<String, Object> errorResponse = new HashMap<>();
443
- errorResponse.put("success", false);
444
- errorResponse.put("message", "Rating not found");
445
- errorResponse.put("error_code", "NOT_FOUND");
446
-
447
- return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse);
448
- }
449
- }
450
- /**
451
- * Check if a booking can be rated by the current user
452
- * GET /api/ratings/can-rate/{bookingId}
453
- * Issue #26: Booking-Rating Integration
454
- */
455
- @GetMapping("/can-rate/{bookingId}")
456
- public ResponseEntity<?> canRateBooking(
457
- @PathVariable @NotBlank(message = "Booking ID is required") String bookingId) {
458
-
459
- String userId = SecurityUtils.getCurrentUserId();
460
- logger.info("Request to check if booking {} can be rated by user: {}", bookingId, userId);
461
-
462
- try {
463
- boolean canRate = ratingService.canRateBooking(bookingId, userId);
464
-
465
- Map<String, Object> response = new HashMap<>();
466
- response.put("success", true);
467
- response.put("can_rate", canRate);
468
- response.put("booking_id", bookingId);
469
-
470
- return ResponseEntity.ok(response);
471
-
472
- } catch (Exception e) {
473
- logger.error("Error checking if booking can be rated: {}", e.getMessage());
474
-
475
- Map<String, Object> errorResponse = new HashMap<>();
476
- errorResponse.put("success", false);
477
- errorResponse.put("message", "Error checking rating eligibility");
478
- errorResponse.put("error_code", "CHECK_ERROR");
479
-
480
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
481
- }
482
- }
483
-
484
-
485
- // Helper methods
486
-
487
- /**
488
- * Convert RatingService.RatingStatistics to RatingStatsResponse DTO
489
- */
490
- private RatingStatsResponse convertToStatsResponse(RatingService.RatingStatistics stats, String subjectId, String subjectType) {
491
- RatingStatsResponse response = new RatingStatsResponse(
492
- subjectId,
493
- subjectType,
494
- stats.getAverageRating(),
495
- (long) stats.getTotalRatings()
496
- );
497
-
498
- // Convert rating distribution array to map
499
- Map<String, Long> distributionMap = new HashMap<>();
500
- int[] distribution = stats.getRatingDistribution();
501
- distributionMap.put("1", (long) distribution[0]);
502
- distributionMap.put("2", (long) distribution[1]);
503
- distributionMap.put("3", (long) distribution[2]);
504
- distributionMap.put("4", (long) distribution[3]);
505
- distributionMap.put("5", (long) distribution[4]);
506
-
507
- response.setRatingDistribution(distributionMap);
508
-
509
- return response;
510
- }
511
-
512
- /**
513
- * Global exception handler for validation errors
514
- */
515
- @ExceptionHandler(org.springframework.web.bind.MethodArgumentNotValidException.class)
516
- public ResponseEntity<?> handleValidationExceptions(
517
- org.springframework.web.bind.MethodArgumentNotValidException ex) {
518
-
519
- Map<String, String> errors = new HashMap<>();
520
- ex.getBindingResult().getFieldErrors().forEach(error ->
521
- errors.put(error.getField(), error.getDefaultMessage()));
522
-
523
- Map<String, Object> errorResponse = new HashMap<>();
524
- errorResponse.put("success", false);
525
- errorResponse.put("message", "Validation failed");
526
- errorResponse.put("error_code", "VALIDATION_ERROR");
527
- errorResponse.put("errors", errors);
528
-
529
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
530
- }
531
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/controller/UserController.java DELETED
@@ -1,100 +0,0 @@
1
- package com.locallend.locallend.controller;
2
-
3
- import com.locallend.locallend.dto.request.UserUpdateDto;
4
- import com.locallend.locallend.dto.response.UserPublicDto;
5
- import com.locallend.locallend.dto.response.UserResponseDto;
6
- import com.locallend.locallend.exception.BusinessException;
7
- import com.locallend.locallend.model.User;
8
- import com.locallend.locallend.service.UserQueryService;
9
- import com.locallend.locallend.service.UserService;
10
- import com.locallend.locallend.util.SecurityUtils;
11
- import com.locallend.locallend.util.UserMapper;
12
- import jakarta.validation.Valid;
13
- import org.springframework.beans.factory.annotation.Autowired;
14
- import org.springframework.data.domain.Page;
15
- import org.springframework.http.ResponseEntity;
16
- import org.springframework.web.bind.annotation.*;
17
-
18
- /**
19
- * REST controller to manage authenticated user operations and public user queries.
20
- * Handles profile management, public info lookup, and user search.
21
- **/
22
-
23
- @RestController
24
- @RequestMapping("/api/users")
25
- public class UserController {
26
-
27
- @Autowired
28
- private UserService userService;
29
-
30
- @Autowired
31
- private UserQueryService userQueryService;
32
-
33
- @Autowired
34
- private UserMapper userMapper;
35
-
36
- /**
37
- * Get current user's profile.
38
- * Extracts user ID from JWT token in SecurityContext.
39
- *
40
- * @return Full user profile
41
- */
42
- @GetMapping("/profile")
43
- public ResponseEntity<UserResponseDto> getProfile() {
44
- String userId = SecurityUtils.getCurrentUserId();
45
- User user = userQueryService.findById(userId)
46
- .orElseThrow(() -> new BusinessException("User not found"));
47
- return ResponseEntity.ok(userMapper.toUserResponseDto(user));
48
- }
49
-
50
- /**
51
- * Update current user's profile.
52
- * Extracts user ID from JWT token in SecurityContext.
53
- *
54
- * @param updateDto Profile update data
55
- * @return Updated user profile
56
- */
57
- @PutMapping("/profile")
58
- public ResponseEntity<UserResponseDto> updateProfile(@Valid @RequestBody UserUpdateDto updateDto) {
59
- String userId = SecurityUtils.getCurrentUserId();
60
- User updated = userService.updateProfile(userId,
61
- updateDto.getName(),
62
- updateDto.getPhoneNumber(),
63
- updateDto.getProfileImageUrl());
64
- return ResponseEntity.ok(userMapper.toUserResponseDto(updated));
65
- }
66
-
67
- /**
68
- * Get public information about any user.
69
- * Does not expose sensitive information like email or phone.
70
- *
71
- * @param id User ID to lookup
72
- * @return Public user information
73
- */
74
- @GetMapping("/{id}/public")
75
- public ResponseEntity<UserPublicDto> getPublicInfo(@PathVariable String id) {
76
- User user = userQueryService.findById(id)
77
- .orElseThrow(() -> new BusinessException("User not found"));
78
- return ResponseEntity.ok(userMapper.toUserPublicDto(user));
79
- }
80
-
81
- /**
82
- * Search active users by name or username.
83
- * Returns paginated results with only public information.
84
- *
85
- * @param term Search term (case-insensitive)
86
- * @param page Page number (default: 0)
87
- * @param size Page size (default: 10)
88
- * @return Page of matching users (public info only)
89
- */
90
- @GetMapping("/search")
91
- public ResponseEntity<Page<UserPublicDto>> search(
92
- @RequestParam String term,
93
- @RequestParam(defaultValue = "0") int page,
94
- @RequestParam(defaultValue = "10") int size) {
95
- Page<User> users = userQueryService.searchActiveUsers(term, page, size);
96
- Page<UserPublicDto> result = users.map(userMapper::toUserPublicDto);
97
- return ResponseEntity.ok(result);
98
- }
99
- }
100
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/dto/CategoryDto.java DELETED
@@ -1,106 +0,0 @@
1
- package com.locallend.locallend.dto;
2
-
3
- import com.fasterxml.jackson.annotation.JsonFormat;
4
- import com.fasterxml.jackson.annotation.JsonProperty;
5
-
6
- import java.time.LocalDateTime;
7
-
8
- public class CategoryDto {
9
-
10
- @JsonProperty("id")
11
- private String id;
12
-
13
- @JsonProperty("name")
14
- private String name;
15
-
16
- @JsonProperty("description")
17
- private String description;
18
-
19
- @JsonProperty("parent_category_id")
20
- private String parentCategoryId;
21
-
22
- @JsonProperty("parent_category_name")
23
- private String parentCategoryName;
24
-
25
- @JsonProperty("is_active")
26
- private boolean isActive;
27
-
28
- @JsonProperty("item_count")
29
- private long itemCount;
30
-
31
- @JsonProperty("has_subcategories")
32
- private boolean hasSubcategories;
33
-
34
- @JsonProperty("created_at")
35
- @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss")
36
- private LocalDateTime createdAt;
37
-
38
- @JsonProperty("updated_at")
39
- @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss")
40
- private LocalDateTime updatedAt;
41
-
42
- public CategoryDto() {}
43
-
44
- public CategoryDto(String id, String name, String description, String parentCategoryId,
45
- String parentCategoryName, boolean isActive, long itemCount,
46
- boolean hasSubcategories, LocalDateTime createdAt, LocalDateTime updatedAt) {
47
- this.id = id;
48
- this.name = name;
49
- this.description = description;
50
- this.parentCategoryId = parentCategoryId;
51
- this.parentCategoryName = parentCategoryName;
52
- this.isActive = isActive;
53
- this.itemCount = itemCount;
54
- this.hasSubcategories = hasSubcategories;
55
- this.createdAt = createdAt;
56
- this.updatedAt = updatedAt;
57
- }
58
-
59
- public static class Builder {
60
- private CategoryDto categoryDto = new CategoryDto();
61
-
62
- public Builder id(String id) { categoryDto.id = id; return this; }
63
- public Builder name(String name) { categoryDto.name = name; return this; }
64
- public Builder description(String description) { categoryDto.description = description; return this; }
65
- public Builder parentCategoryId(String parentCategoryId) { categoryDto.parentCategoryId = parentCategoryId; return this; }
66
- public Builder parentCategoryName(String parentCategoryName) { categoryDto.parentCategoryName = parentCategoryName; return this; }
67
- public Builder isActive(boolean isActive) { categoryDto.isActive = isActive; return this; }
68
- public Builder itemCount(long itemCount) { categoryDto.itemCount = itemCount; return this; }
69
- public Builder hasSubcategories(boolean hasSubcategories) { categoryDto.hasSubcategories = hasSubcategories; return this; }
70
- public Builder createdAt(LocalDateTime createdAt) { categoryDto.createdAt = createdAt; return this; }
71
- public Builder updatedAt(LocalDateTime updatedAt) { categoryDto.updatedAt = updatedAt; return this; }
72
- public CategoryDto build() { return categoryDto; }
73
- }
74
-
75
- public static Builder builder() { return new Builder(); }
76
-
77
- public String getId() { return id; }
78
- public void setId(String id) { this.id = id; }
79
-
80
- public String getName() { return name; }
81
- public void setName(String name) { this.name = name; }
82
-
83
- public String getDescription() { return description; }
84
- public void setDescription(String description) { this.description = description; }
85
-
86
- public String getParentCategoryId() { return parentCategoryId; }
87
- public void setParentCategoryId(String parentCategoryId) { this.parentCategoryId = parentCategoryId; }
88
-
89
- public String getParentCategoryName() { return parentCategoryName; }
90
- public void setParentCategoryName(String parentCategoryName) { this.parentCategoryName = parentCategoryName; }
91
-
92
- public boolean isActive() { return isActive; }
93
- public void setActive(boolean active) { isActive = active; }
94
-
95
- public long getItemCount() { return itemCount; }
96
- public void setItemCount(long itemCount) { this.itemCount = itemCount; }
97
-
98
- public boolean isHasSubcategories() { return hasSubcategories; }
99
- public void setHasSubcategories(boolean hasSubcategories) { this.hasSubcategories = hasSubcategories; }
100
-
101
- public LocalDateTime getCreatedAt() { return createdAt; }
102
- public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
103
-
104
- public LocalDateTime getUpdatedAt() { return updatedAt; }
105
- public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
106
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/dto/CreateCategoryRequest.java DELETED
@@ -1,57 +0,0 @@
1
- package com.locallend.locallend.dto;
2
-
3
- import com.fasterxml.jackson.annotation.JsonProperty;
4
- import jakarta.validation.constraints.NotBlank;
5
- import jakarta.validation.constraints.Size;
6
- import jakarta.validation.constraints.Pattern;
7
-
8
- public class CreateCategoryRequest {
9
-
10
- @NotBlank(message = "Category name is required")
11
- @Size(min = 2, max = 50, message = "Category name must be between 2 and 50 characters")
12
- @Pattern(regexp = "^[a-zA-Z0-9\\s&-_.()]+$", message = "Category name contains invalid characters")
13
- @JsonProperty("name")
14
- private String name;
15
-
16
- @Size(max = 200, message = "Description cannot exceed 200 characters")
17
- @JsonProperty("description")
18
- private String description;
19
-
20
- @Size(max = 24, message = "Invalid parent category ID format")
21
- @JsonProperty("parent_category_id")
22
- private String parentCategoryId;
23
-
24
- public CreateCategoryRequest() {}
25
-
26
- public CreateCategoryRequest(String name, String description) {
27
- this.name = name;
28
- this.description = description;
29
- }
30
-
31
- public CreateCategoryRequest(String name, String description, String parentCategoryId) {
32
- this.name = name;
33
- this.description = description;
34
- this.parentCategoryId = parentCategoryId;
35
- }
36
-
37
- public String getName() { return name; }
38
- public void setName(String name) { this.name = name != null ? name.trim() : null; }
39
-
40
- public String getDescription() { return description; }
41
- public void setDescription(String description) { this.description = description != null ? description.trim() : null; }
42
-
43
- public String getParentCategoryId() { return parentCategoryId; }
44
- public void setParentCategoryId(String parentCategoryId) { this.parentCategoryId = parentCategoryId != null ? parentCategoryId.trim() : null; }
45
-
46
- public boolean hasParentCategory() { return parentCategoryId != null && !parentCategoryId.isEmpty(); }
47
- public boolean hasDescription() { return description != null && !description.isEmpty(); }
48
-
49
- @Override
50
- public String toString() {
51
- return "CreateCategoryRequest{" +
52
- "name='" + name + '\'' +
53
- ", description='" + description + '\'' +
54
- ", parentCategoryId='" + parentCategoryId + '\'' +
55
- '}';
56
- }
57
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/dto/CreateItemRequest.java DELETED
@@ -1,49 +0,0 @@
1
- package com.locallend.locallend.dto;
2
-
3
- import jakarta.validation.constraints.DecimalMin;
4
- import jakarta.validation.constraints.NotBlank;
5
- import jakarta.validation.constraints.NotNull;
6
- import jakarta.validation.constraints.Size;
7
-
8
- import java.util.List;
9
-
10
- public class CreateItemRequest {
11
-
12
- @NotBlank
13
- @Size(min = 3, max = 100)
14
- private String name;
15
-
16
- @Size(max = 500)
17
- private String description;
18
-
19
- @NotNull
20
- private String categoryId;
21
-
22
- @DecimalMin(value = "0.0", inclusive = true)
23
- private Double deposit = 0.0;
24
-
25
- private List<String> images;
26
-
27
- private String condition; // NEW|EXCELLENT|GOOD|FAIR|POOR
28
-
29
- public CreateItemRequest() {}
30
-
31
- // Getters and setters
32
- public String getName() { return name; }
33
- public void setName(String name) { this.name = name; }
34
-
35
- public String getDescription() { return description; }
36
- public void setDescription(String description) { this.description = description; }
37
-
38
- public String getCategoryId() { return categoryId; }
39
- public void setCategoryId(String categoryId) { this.categoryId = categoryId; }
40
-
41
- public Double getDeposit() { return deposit; }
42
- public void setDeposit(Double deposit) { this.deposit = deposit; }
43
-
44
- public List<String> getImages() { return images; }
45
- public void setImages(List<String> images) { this.images = images; }
46
-
47
- public String getCondition() { return condition; }
48
- public void setCondition(String condition) { this.condition = condition; }
49
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/dto/ItemDTO.java DELETED
@@ -1,68 +0,0 @@
1
- package com.locallend.locallend.dto;
2
-
3
- import java.time.LocalDateTime;
4
- import java.util.List;
5
-
6
- public class ItemDTO {
7
- private String id;
8
- private String name;
9
- private String description;
10
- private String condition;
11
- private String status;
12
- private double deposit;
13
- private List<String> images;
14
- private double averageRating;
15
- private String ownerId;
16
- private String ownerName;
17
- private String categoryId;
18
- private String categoryName;
19
- private boolean canBeBorrowed;
20
- private LocalDateTime createdAt;
21
- private LocalDateTime updatedAt;
22
-
23
- // Getters and setters
24
- public String getId() { return id; }
25
- public void setId(String id) { this.id = id; }
26
-
27
- public String getName() { return name; }
28
- public void setName(String name) { this.name = name; }
29
-
30
- public String getDescription() { return description; }
31
- public void setDescription(String description) { this.description = description; }
32
-
33
- public String getCondition() { return condition; }
34
- public void setCondition(String condition) { this.condition = condition; }
35
-
36
- public String getStatus() { return status; }
37
- public void setStatus(String status) { this.status = status; }
38
-
39
- public double getDeposit() { return deposit; }
40
- public void setDeposit(double deposit) { this.deposit = deposit; }
41
-
42
- public List<String> getImages() { return images; }
43
- public void setImages(List<String> images) { this.images = images; }
44
-
45
- public double getAverageRating() { return averageRating; }
46
- public void setAverageRating(double averageRating) { this.averageRating = averageRating; }
47
-
48
- public String getOwnerId() { return ownerId; }
49
- public void setOwnerId(String ownerId) { this.ownerId = ownerId; }
50
-
51
- public String getOwnerName() { return ownerName; }
52
- public void setOwnerName(String ownerName) { this.ownerName = ownerName; }
53
-
54
- public String getCategoryId() { return categoryId; }
55
- public void setCategoryId(String categoryId) { this.categoryId = categoryId; }
56
-
57
- public String getCategoryName() { return categoryName; }
58
- public void setCategoryName(String categoryName) { this.categoryName = categoryName; }
59
-
60
- public boolean isCanBeBorrowed() { return canBeBorrowed; }
61
- public void setCanBeBorrowed(boolean canBeBorrowed) { this.canBeBorrowed = canBeBorrowed; }
62
-
63
- public LocalDateTime getCreatedAt() { return createdAt; }
64
- public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
65
-
66
- public LocalDateTime getUpdatedAt() { return updatedAt; }
67
- public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
68
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/dto/UpdateItemRequest.java DELETED
@@ -1,33 +0,0 @@
1
- package com.locallend.locallend.dto;
2
-
3
- import java.util.List;
4
-
5
- public class UpdateItemRequest {
6
- private String name;
7
- private String description;
8
- private String condition;
9
- private Double deposit;
10
- private List<String> images;
11
- private Boolean isAvailable;
12
-
13
- public UpdateItemRequest() {}
14
-
15
- // Getters and setters
16
- public String getName() { return name; }
17
- public void setName(String name) { this.name = name; }
18
-
19
- public String getDescription() { return description; }
20
- public void setDescription(String description) { this.description = description; }
21
-
22
- public String getCondition() { return condition; }
23
- public void setCondition(String condition) { this.condition = condition; }
24
-
25
- public Double getDeposit() { return deposit; }
26
- public void setDeposit(Double deposit) { this.deposit = deposit; }
27
-
28
- public List<String> getImages() { return images; }
29
- public void setImages(List<String> images) { this.images = images; }
30
-
31
- public Boolean getIsAvailable() { return isAvailable; }
32
- public void setIsAvailable(Boolean isAvailable) { this.isAvailable = isAvailable; }
33
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/dto/request/BookingRequestDto.java DELETED
@@ -1,78 +0,0 @@
1
- package com.locallend.locallend.dto.request;
2
-
3
- import jakarta.validation.constraints.*;
4
- import java.time.LocalDateTime;
5
-
6
- /**
7
- * DTO for creating new bookings in the LocalLend platform.
8
- */
9
- public class BookingRequestDto {
10
-
11
- @NotBlank(message = "Item ID is required")
12
- private String itemId;
13
-
14
- @NotNull(message = "Start date is required")
15
- @Future(message = "Start date must be in the future")
16
- private LocalDateTime startDate;
17
-
18
- @NotNull(message = "End date is required")
19
- private LocalDateTime endDate;
20
-
21
- @Size(max = 500, message = "Booking notes cannot exceed 500 characters")
22
- private String bookingNotes;
23
-
24
- @DecimalMin(value = "0.0", message = "Deposit amount cannot be negative")
25
- @DecimalMax(value = "10000.0", message = "Deposit amount cannot exceed 10,000")
26
- private Double depositAmount;
27
-
28
- @Min(value = 1, message = "Duration must be at least 1 day")
29
- @Max(value = 365, message = "Duration cannot exceed 365 days")
30
- private Integer requestedDurationDays;
31
-
32
- private Boolean acceptTerms = false;
33
-
34
- public BookingRequestDto() {}
35
-
36
- public BookingRequestDto(String itemId, LocalDateTime startDate, LocalDateTime endDate) {
37
- this.itemId = itemId;
38
- this.startDate = startDate;
39
- this.endDate = endDate;
40
- }
41
-
42
- @AssertTrue(message = "End date must be after start date")
43
- public boolean isValidDateRange() {
44
- if (startDate == null || endDate == null) return true;
45
- return endDate.isAfter(startDate);
46
- }
47
-
48
- @AssertTrue(message = "Must accept terms and conditions")
49
- public boolean isTermsAccepted() {
50
- return acceptTerms != null && acceptTerms;
51
- }
52
-
53
- public String getItemId() { return itemId; }
54
- public void setItemId(String itemId) { this.itemId = itemId; }
55
- public LocalDateTime getStartDate() { return startDate; }
56
- public void setStartDate(LocalDateTime startDate) { this.startDate = startDate; }
57
- public LocalDateTime getEndDate() { return endDate; }
58
- public void setEndDate(LocalDateTime endDate) { this.endDate = endDate; }
59
- public String getBookingNotes() { return bookingNotes; }
60
- public void setBookingNotes(String bookingNotes) { this.bookingNotes = bookingNotes; }
61
- public Double getDepositAmount() { return depositAmount; }
62
- public void setDepositAmount(Double depositAmount) { this.depositAmount = depositAmount; }
63
- public Integer getRequestedDurationDays() { return requestedDurationDays; }
64
- public void setRequestedDurationDays(Integer requestedDurationDays) { this.requestedDurationDays = requestedDurationDays; }
65
- public Boolean getAcceptTerms() { return acceptTerms; }
66
- public void setAcceptTerms(Boolean acceptTerms) { this.acceptTerms = acceptTerms; }
67
-
68
- @Override
69
- public String toString() {
70
- return "BookingRequestDto{" +
71
- "itemId='" + itemId + '\'' +
72
- ", startDate=" + startDate +
73
- ", endDate=" + endDate +
74
- ", requestedDurationDays=" + requestedDurationDays +
75
- ", acceptTerms=" + acceptTerms +
76
- '}';
77
- }
78
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/dto/request/ItemRequestDto.java DELETED
@@ -1,60 +0,0 @@
1
- package com.locallend.locallend.dto.request;
2
-
3
- import com.fasterxml.jackson.annotation.JsonProperty;
4
- import jakarta.validation.constraints.Min;
5
- import jakarta.validation.constraints.NotBlank;
6
- import jakarta.validation.constraints.Size;
7
-
8
- import java.util.List;
9
-
10
- public class ItemRequestDto {
11
-
12
- @NotBlank(message = "Item name is required")
13
- @Size(min = 2, max = 100)
14
- @JsonProperty("name")
15
- private String name;
16
-
17
- @Size(max = 1000)
18
- @JsonProperty("description")
19
- private String description;
20
-
21
- @JsonProperty("condition")
22
- private String condition;
23
-
24
- @Min(value = 0, message = "Deposit must be non-negative")
25
- @JsonProperty("deposit")
26
- private double deposit = 0.0;
27
-
28
- @JsonProperty("images")
29
- private List<String> images;
30
-
31
- @JsonProperty("category_id")
32
- private String categoryId;
33
-
34
- @JsonProperty("owner_id")
35
- private String ownerId;
36
-
37
- public ItemRequestDto() {}
38
-
39
- // Getters and setters
40
- public String getName() { return name; }
41
- public void setName(String name) { this.name = name; }
42
-
43
- public String getDescription() { return description; }
44
- public void setDescription(String description) { this.description = description; }
45
-
46
- public String getCondition() { return condition; }
47
- public void setCondition(String condition) { this.condition = condition; }
48
-
49
- public double getDeposit() { return deposit; }
50
- public void setDeposit(double deposit) { this.deposit = deposit; }
51
-
52
- public List<String> getImages() { return images; }
53
- public void setImages(List<String> images) { this.images = images; }
54
-
55
- public String getCategoryId() { return categoryId; }
56
- public void setCategoryId(String categoryId) { this.categoryId = categoryId; }
57
-
58
- public String getOwnerId() { return ownerId; }
59
- public void setOwnerId(String ownerId) { this.ownerId = ownerId; }
60
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/dto/request/ItemUpdateDto.java DELETED
@@ -1,49 +0,0 @@
1
- package com.locallend.locallend.dto.request;
2
-
3
- import com.fasterxml.jackson.annotation.JsonProperty;
4
-
5
- import java.util.List;
6
-
7
- /**
8
- * Partial update DTO for Item
9
- */
10
- public class ItemUpdateDto {
11
-
12
- @JsonProperty("name")
13
- private String name;
14
-
15
- @JsonProperty("description")
16
- private String description;
17
-
18
- @JsonProperty("condition")
19
- private String condition;
20
-
21
- @JsonProperty("deposit")
22
- private Double deposit;
23
-
24
- @JsonProperty("images")
25
- private List<String> images;
26
-
27
- @JsonProperty("status")
28
- private String status;
29
-
30
- public ItemUpdateDto() {}
31
-
32
- public String getName() { return name; }
33
- public void setName(String name) { this.name = name; }
34
-
35
- public String getDescription() { return description; }
36
- public void setDescription(String description) { this.description = description; }
37
-
38
- public String getCondition() { return condition; }
39
- public void setCondition(String condition) { this.condition = condition; }
40
-
41
- public Double getDeposit() { return deposit; }
42
- public void setDeposit(Double deposit) { this.deposit = deposit; }
43
-
44
- public List<String> getImages() { return images; }
45
- public void setImages(List<String> images) { this.images = images; }
46
-
47
- public String getStatus() { return status; }
48
- public void setStatus(String status) { this.status = status; }
49
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/dto/request/RatingCreateDto.java DELETED
@@ -1,71 +0,0 @@
1
- package com.locallend.locallend.dto.request;
2
-
3
- import com.locallend.locallend.model.RatingType;
4
- import jakarta.validation.constraints.*;
5
-
6
- /**
7
- * DTO for creating a new rating.
8
- * Includes validation for rating values and cross-field validation for item ratings.
9
- * Issue #23: Rating Entity, Repository, DTO
10
- */
11
- public class RatingCreateDto {
12
-
13
- @NotBlank(message = "Ratee ID is required")
14
- private String rateeId;
15
-
16
- private String itemId;
17
- private String bookingId;
18
-
19
- @NotNull(message = "Rating type is required")
20
- private RatingType ratingType;
21
-
22
- @NotNull(message = "Rating value is required")
23
- @Min(value = 1, message = "Rating must be between 1 and 5")
24
- @Max(value = 5, message = "Rating must be between 1 and 5")
25
- private Integer ratingValue;
26
-
27
- @Size(max = 1000, message = "Comment cannot exceed 1000 characters")
28
- private String comment;
29
-
30
- private Boolean isAnonymous = false;
31
-
32
- // Constructors
33
- public RatingCreateDto() {}
34
-
35
- public RatingCreateDto(String rateeId, RatingType ratingType, Integer ratingValue) {
36
- this.rateeId = rateeId;
37
- this.ratingType = ratingType;
38
- this.ratingValue = ratingValue;
39
- }
40
-
41
- // Cross-field validation
42
- @AssertTrue(message = "Item ID is required for item ratings")
43
- public boolean isItemIdValidForType() {
44
- if (ratingType == RatingType.USER_TO_ITEM) {
45
- return itemId != null && !itemId.trim().isEmpty();
46
- }
47
- return true;
48
- }
49
-
50
- // Getters and Setters
51
- public String getRateeId() { return rateeId; }
52
- public void setRateeId(String rateeId) { this.rateeId = rateeId; }
53
-
54
- public String getItemId() { return itemId; }
55
- public void setItemId(String itemId) { this.itemId = itemId; }
56
-
57
- public String getBookingId() { return bookingId; }
58
- public void setBookingId(String bookingId) { this.bookingId = bookingId; }
59
-
60
- public RatingType getRatingType() { return ratingType; }
61
- public void setRatingType(RatingType ratingType) { this.ratingType = ratingType; }
62
-
63
- public Integer getRatingValue() { return ratingValue; }
64
- public void setRatingValue(Integer ratingValue) { this.ratingValue = ratingValue; }
65
-
66
- public String getComment() { return comment; }
67
- public void setComment(String comment) { this.comment = comment; }
68
-
69
- public Boolean getIsAnonymous() { return isAnonymous; }
70
- public void setIsAnonymous(Boolean isAnonymous) { this.isAnonymous = isAnonymous; }
71
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/dto/request/UserLoginDto.java DELETED
@@ -1,33 +0,0 @@
1
- package com.locallend.locallend.dto.request;
2
-
3
- import jakarta.validation.constraints.NotBlank;
4
-
5
- /**
6
- * DTO for user login request.
7
- */
8
- public class UserLoginDto {
9
- @NotBlank(message = "Username or email is required")
10
- private String usernameOrEmail;
11
-
12
- @NotBlank(message = "Password is required")
13
- private String password;
14
-
15
- public UserLoginDto() {}
16
-
17
- // Getters and Setters
18
- public String getUsernameOrEmail() {
19
- return usernameOrEmail;
20
- }
21
-
22
- public void setUsernameOrEmail(String usernameOrEmail) {
23
- this.usernameOrEmail = usernameOrEmail;
24
- }
25
-
26
- public String getPassword() {
27
- return password;
28
- }
29
-
30
- public void setPassword(String password) {
31
- this.password = password;
32
- }
33
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/dto/request/UserRegistrationDto.java DELETED
@@ -1,71 +0,0 @@
1
- package com.locallend.locallend.dto.request;
2
-
3
- import jakarta.validation.constraints.Email;
4
- import jakarta.validation.constraints.NotBlank;
5
- import jakarta.validation.constraints.Size;
6
-
7
- /**
8
- * DTO for user registration request.
9
- */
10
- public class UserRegistrationDto {
11
- @NotBlank(message = "Username is required")
12
- @Size(min = 3, max = 50, message = "Username must be between 3 and 50 characters")
13
- private String username;
14
-
15
- @NotBlank(message = "Name is required")
16
- @Size(min = 2, max = 100, message = "Name must be between 2 and 100 characters")
17
- private String name;
18
-
19
- @NotBlank(message = "Email is required")
20
- @Email(message = "Email must be valid")
21
- private String email;
22
-
23
- @NotBlank(message = "Password is required")
24
- @Size(min = 8, message = "Password must be at least 8 characters")
25
- private String password;
26
-
27
- private String phoneNumber;
28
-
29
- public UserRegistrationDto() {}
30
-
31
- // Getters and Setters
32
- public String getUsername() {
33
- return username;
34
- }
35
-
36
- public void setUsername(String username) {
37
- this.username = username;
38
- }
39
-
40
- public String getName() {
41
- return name;
42
- }
43
-
44
- public void setName(String name) {
45
- this.name = name;
46
- }
47
-
48
- public String getEmail() {
49
- return email;
50
- }
51
-
52
- public void setEmail(String email) {
53
- this.email = email;
54
- }
55
-
56
- public String getPassword() {
57
- return password;
58
- }
59
-
60
- public void setPassword(String password) {
61
- this.password = password;
62
- }
63
-
64
- public String getPhoneNumber() {
65
- return phoneNumber;
66
- }
67
-
68
- public void setPhoneNumber(String phoneNumber) {
69
- this.phoneNumber = phoneNumber;
70
- }
71
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/dto/request/UserUpdateDto.java DELETED
@@ -1,42 +0,0 @@
1
- package com.locallend.locallend.dto.request;
2
-
3
- import jakarta.validation.constraints.Size;
4
-
5
- /**
6
- * DTO for user profile update requests.
7
- */
8
- public class UserUpdateDto {
9
- @Size(min = 2, max = 100, message = "Name must be between 2 and 100 characters")
10
- private String name;
11
-
12
- private String phoneNumber;
13
-
14
- private String profileImageUrl;
15
-
16
- public UserUpdateDto() {}
17
-
18
- // Getters and Setters
19
- public String getName() {
20
- return name;
21
- }
22
-
23
- public void setName(String name) {
24
- this.name = name;
25
- }
26
-
27
- public String getPhoneNumber() {
28
- return phoneNumber;
29
- }
30
-
31
- public void setPhoneNumber(String phoneNumber) {
32
- this.phoneNumber = phoneNumber;
33
- }
34
-
35
- public String getProfileImageUrl() {
36
- return profileImageUrl;
37
- }
38
-
39
- public void setProfileImageUrl(String profileImageUrl) {
40
- this.profileImageUrl = profileImageUrl;
41
- }
42
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/dto/response/BookingResponseDto.java DELETED
@@ -1,138 +0,0 @@
1
- package com.locallend.locallend.dto.response;
2
-
3
- import com.locallend.locallend.model.enums.BookingStatus;
4
- import java.time.LocalDateTime;
5
-
6
- /**
7
- * DTO for returning complete booking information to API consumers.
8
- */
9
- public class BookingResponseDto {
10
-
11
- private String id;
12
- private String itemId;
13
- private String itemName;
14
- private String itemImageUrl;
15
- private String borrowerId;
16
- private String borrowerName;
17
- private String ownerId;
18
- private String ownerName;
19
- private BookingStatus status;
20
- private LocalDateTime startDate;
21
- private LocalDateTime endDate;
22
- private LocalDateTime actualStartDate;
23
- private LocalDateTime actualEndDate;
24
- private String bookingNotes;
25
- private String ownerNotes;
26
- private Double depositAmount;
27
- private Boolean depositPaid;
28
- private LocalDateTime createdDate;
29
- private LocalDateTime updatedDate;
30
- private LocalDateTime confirmedDate;
31
- private LocalDateTime pickupDate;
32
- private LocalDateTime returnDate;
33
- private LocalDateTime cancelledDate;
34
- private String cancellationReason;
35
- private Boolean isRated;
36
- private Integer durationDays;
37
-
38
- // Computed fields
39
- private String statusDescription;
40
- private String timeAgo;
41
- private Long daysUntilStart;
42
- private Long daysUntilEnd;
43
- private Boolean canBeCancelled;
44
- private Boolean canBeConfirmed;
45
- private Boolean canBeActivated;
46
- private Boolean canBeCompleted;
47
- private Boolean isOverdue;
48
- private Boolean requiresDeposit;
49
-
50
- public BookingResponseDto() {}
51
-
52
- public String getId() { return id; }
53
- public void setId(String id) { this.id = id; }
54
- public String getItemId() { return itemId; }
55
- public void setItemId(String itemId) { this.itemId = itemId; }
56
- public String getItemName() { return itemName; }
57
- public void setItemName(String itemName) { this.itemName = itemName; }
58
- public String getItemImageUrl() { return itemImageUrl; }
59
- public void setItemImageUrl(String itemImageUrl) { this.itemImageUrl = itemImageUrl; }
60
- public String getBorrowerId() { return borrowerId; }
61
- public void setBorrowerId(String borrowerId) { this.borrowerId = borrowerId; }
62
- public String getBorrowerName() { return borrowerName; }
63
- public void setBorrowerName(String borrowerName) { this.borrowerName = borrowerName; }
64
- public String getOwnerId() { return ownerId; }
65
- public void setOwnerId(String ownerId) { this.ownerId = ownerId; }
66
- public String getOwnerName() { return ownerName; }
67
- public void setOwnerName(String ownerName) { this.ownerName = ownerName; }
68
- public BookingStatus getStatus() { return status; }
69
- public void setStatus(BookingStatus status) { this.status = status; }
70
- public LocalDateTime getStartDate() { return startDate; }
71
- public void setStartDate(LocalDateTime startDate) { this.startDate = startDate; }
72
- public LocalDateTime getEndDate() { return endDate; }
73
- public void setEndDate(LocalDateTime endDate) { this.endDate = endDate; }
74
- public LocalDateTime getActualStartDate() { return actualStartDate; }
75
- public void setActualStartDate(LocalDateTime actualStartDate) { this.actualStartDate = actualStartDate; }
76
- public LocalDateTime getActualEndDate() { return actualEndDate; }
77
- public void setActualEndDate(LocalDateTime actualEndDate) { this.actualEndDate = actualEndDate; }
78
- public String getBookingNotes() { return bookingNotes; }
79
- public void setBookingNotes(String bookingNotes) { this.bookingNotes = bookingNotes; }
80
- public String getOwnerNotes() { return ownerNotes; }
81
- public void setOwnerNotes(String ownerNotes) { this.ownerNotes = ownerNotes; }
82
- public Double getDepositAmount() { return depositAmount; }
83
- public void setDepositAmount(Double depositAmount) { this.depositAmount = depositAmount; }
84
- public Boolean getDepositPaid() { return depositPaid; }
85
- public void setDepositPaid(Boolean depositPaid) { this.depositPaid = depositPaid; }
86
- public LocalDateTime getCreatedDate() { return createdDate; }
87
- public void setCreatedDate(LocalDateTime createdDate) { this.createdDate = createdDate; }
88
- public LocalDateTime getUpdatedDate() { return updatedDate; }
89
- public void setUpdatedDate(LocalDateTime updatedDate) { this.updatedDate = updatedDate; }
90
- public LocalDateTime getConfirmedDate() { return confirmedDate; }
91
- public void setConfirmedDate(LocalDateTime confirmedDate) { this.confirmedDate = confirmedDate; }
92
- public LocalDateTime getPickupDate() { return pickupDate; }
93
- public void setPickupDate(LocalDateTime pickupDate) { this.pickupDate = pickupDate; }
94
- public LocalDateTime getReturnDate() { return returnDate; }
95
- public void setReturnDate(LocalDateTime returnDate) { this.returnDate = returnDate; }
96
- public LocalDateTime getCancelledDate() { return cancelledDate; }
97
- public void setCancelledDate(LocalDateTime cancelledDate) { this.cancelledDate = cancelledDate; }
98
- public String getCancellationReason() { return cancellationReason; }
99
- public void setCancellationReason(String cancellationReason) { this.cancellationReason = cancellationReason; }
100
- public Boolean getIsRated() { return isRated; }
101
- public void setIsRated(Boolean isRated) { this.isRated = isRated; }
102
- public Integer getDurationDays() { return durationDays; }
103
- public void setDurationDays(Integer durationDays) { this.durationDays = durationDays; }
104
- public String getStatusDescription() { return statusDescription; }
105
- public void setStatusDescription(String statusDescription) { this.statusDescription = statusDescription; }
106
- public String getTimeAgo() { return timeAgo; }
107
- public void setTimeAgo(String timeAgo) { this.timeAgo = timeAgo; }
108
- public Long getDaysUntilStart() { return daysUntilStart; }
109
- public void setDaysUntilStart(Long daysUntilStart) { this.daysUntilStart = daysUntilStart; }
110
- public Long getDaysUntilEnd() { return daysUntilEnd; }
111
- public void setDaysUntilEnd(Long daysUntilEnd) { this.daysUntilEnd = daysUntilEnd; }
112
- public Boolean getCanBeCancelled() { return canBeCancelled; }
113
- public void setCanBeCancelled(Boolean canBeCancelled) { this.canBeCancelled = canBeCancelled; }
114
- public Boolean getCanBeConfirmed() { return canBeConfirmed; }
115
- public void setCanBeConfirmed(Boolean canBeConfirmed) { this.canBeConfirmed = canBeConfirmed; }
116
- public Boolean getCanBeActivated() { return canBeActivated; }
117
- public void setCanBeActivated(Boolean canBeActivated) { this.canBeActivated = canBeActivated; }
118
- public Boolean getCanBeCompleted() { return canBeCompleted; }
119
- public void setCanBeCompleted(Boolean canBeCompleted) { this.canBeCompleted = canBeCompleted; }
120
- public Boolean getIsOverdue() { return isOverdue; }
121
- public void setIsOverdue(Boolean isOverdue) { this.isOverdue = isOverdue; }
122
- public Boolean getRequiresDeposit() { return requiresDeposit; }
123
- public void setRequiresDeposit(Boolean requiresDeposit) { this.requiresDeposit = requiresDeposit; }
124
-
125
- @Override
126
- public String toString() {
127
- return "BookingResponseDto{" +
128
- "id='" + id + '\'' +
129
- ", itemId='" + itemId + '\'' +
130
- ", borrowerId='" + borrowerId + '\'' +
131
- ", ownerId='" + ownerId + '\'' +
132
- ", status=" + status +
133
- ", startDate=" + startDate +
134
- ", endDate=" + endDate +
135
- ", durationDays=" + durationDays +
136
- '}';
137
- }
138
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/dto/response/CategoryResponseDto.java DELETED
@@ -1,36 +0,0 @@
1
- package com.locallend.locallend.dto.response;
2
-
3
- import com.fasterxml.jackson.annotation.JsonProperty;
4
-
5
- public class CategoryResponseDto {
6
-
7
- @JsonProperty("id")
8
- private String id;
9
-
10
- @JsonProperty("name")
11
- private String name;
12
-
13
- @JsonProperty("description")
14
- private String description;
15
-
16
- @JsonProperty("item_count")
17
- private long itemCount;
18
-
19
- @JsonProperty("is_active")
20
- private boolean isActive;
21
-
22
- public String getId() { return id; }
23
- public void setId(String id) { this.id = id; }
24
-
25
- public String getName() { return name; }
26
- public void setName(String name) { this.name = name; }
27
-
28
- public String getDescription() { return description; }
29
- public void setDescription(String description) { this.description = description; }
30
-
31
- public long getItemCount() { return itemCount; }
32
- public void setItemCount(long itemCount) { this.itemCount = itemCount; }
33
-
34
- public boolean isActive() { return isActive; }
35
- public void setActive(boolean active) { isActive = active; }
36
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/dto/response/ItemResponseDto.java DELETED
@@ -1,103 +0,0 @@
1
- package com.locallend.locallend.dto.response;
2
-
3
- import com.fasterxml.jackson.annotation.JsonFormat;
4
- import com.fasterxml.jackson.annotation.JsonProperty;
5
-
6
- import java.time.LocalDateTime;
7
- import java.util.List;
8
-
9
- public class ItemResponseDto {
10
-
11
- @JsonProperty("id")
12
- private String id;
13
-
14
- @JsonProperty("name")
15
- private String name;
16
-
17
- @JsonProperty("description")
18
- private String description;
19
-
20
- @JsonProperty("condition")
21
- private String condition;
22
-
23
- @JsonProperty("status")
24
- private String status;
25
-
26
- @JsonProperty("deposit")
27
- private double deposit;
28
-
29
- @JsonProperty("images")
30
- private List<String> images;
31
-
32
- @JsonProperty("average_rating")
33
- private double averageRating;
34
-
35
- @JsonProperty("owner_id")
36
- private String ownerId;
37
-
38
- @JsonProperty("owner_name")
39
- private String ownerName;
40
-
41
- @JsonProperty("category_id")
42
- private String categoryId;
43
-
44
- @JsonProperty("category_name")
45
- private String categoryName;
46
-
47
- @JsonProperty("can_be_borrowed")
48
- private boolean canBeBorrowed;
49
-
50
- @JsonProperty("created_at")
51
- @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss")
52
- private LocalDateTime createdAt;
53
-
54
- @JsonProperty("updated_at")
55
- @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss")
56
- private LocalDateTime updatedAt;
57
-
58
- // Getters and setters
59
- public String getId() { return id; }
60
- public void setId(String id) { this.id = id; }
61
-
62
- public String getName() { return name; }
63
- public void setName(String name) { this.name = name; }
64
-
65
- public String getDescription() { return description; }
66
- public void setDescription(String description) { this.description = description; }
67
-
68
- public String getCondition() { return condition; }
69
- public void setCondition(String condition) { this.condition = condition; }
70
-
71
- public String getStatus() { return status; }
72
- public void setStatus(String status) { this.status = status; }
73
-
74
- public double getDeposit() { return deposit; }
75
- public void setDeposit(double deposit) { this.deposit = deposit; }
76
-
77
- public List<String> getImages() { return images; }
78
- public void setImages(List<String> images) { this.images = images; }
79
-
80
- public double getAverageRating() { return averageRating; }
81
- public void setAverageRating(double averageRating) { this.averageRating = averageRating; }
82
-
83
- public String getOwnerId() { return ownerId; }
84
- public void setOwnerId(String ownerId) { this.ownerId = ownerId; }
85
-
86
- public String getOwnerName() { return ownerName; }
87
- public void setOwnerName(String ownerName) { this.ownerName = ownerName; }
88
-
89
- public String getCategoryId() { return categoryId; }
90
- public void setCategoryId(String categoryId) { this.categoryId = categoryId; }
91
-
92
- public String getCategoryName() { return categoryName; }
93
- public void setCategoryName(String categoryName) { this.categoryName = categoryName; }
94
-
95
- public boolean isCanBeBorrowed() { return canBeBorrowed; }
96
- public void setCanBeBorrowed(boolean canBeBorrowed) { this.canBeBorrowed = canBeBorrowed; }
97
-
98
- public LocalDateTime getCreatedAt() { return createdAt; }
99
- public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
100
-
101
- public LocalDateTime getUpdatedAt() { return updatedAt; }
102
- public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
103
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/dto/response/ItemSummaryDto.java DELETED
@@ -1,61 +0,0 @@
1
- package com.locallend.locallend.dto.response;
2
-
3
- import com.fasterxml.jackson.annotation.JsonProperty;
4
-
5
- public class ItemSummaryDto {
6
-
7
- @JsonProperty("id")
8
- private String id;
9
-
10
- @JsonProperty("name")
11
- private String name;
12
-
13
- @JsonProperty("thumbnail")
14
- private String thumbnail;
15
-
16
- @JsonProperty("category_name")
17
- private String categoryName;
18
-
19
- @JsonProperty("owner_name")
20
- private String ownerName;
21
-
22
- @JsonProperty("status")
23
- private String status;
24
-
25
- @JsonProperty("condition")
26
- private String condition;
27
-
28
- @JsonProperty("deposit")
29
- private double deposit;
30
-
31
- @JsonProperty("average_rating")
32
- private double averageRating;
33
-
34
- // Getters and setters
35
- public String getId() { return id; }
36
- public void setId(String id) { this.id = id; }
37
-
38
- public String getName() { return name; }
39
- public void setName(String name) { this.name = name; }
40
-
41
- public String getThumbnail() { return thumbnail; }
42
- public void setThumbnail(String thumbnail) { this.thumbnail = thumbnail; }
43
-
44
- public String getCategoryName() { return categoryName; }
45
- public void setCategoryName(String categoryName) { this.categoryName = categoryName; }
46
-
47
- public String getOwnerName() { return ownerName; }
48
- public void setOwnerName(String ownerName) { this.ownerName = ownerName; }
49
-
50
- public String getStatus() { return status; }
51
- public void setStatus(String status) { this.status = status; }
52
-
53
- public String getCondition() { return condition; }
54
- public void setCondition(String condition) { this.condition = condition; }
55
-
56
- public double getDeposit() { return deposit; }
57
- public void setDeposit(double deposit) { this.deposit = deposit; }
58
-
59
- public double getAverageRating() { return averageRating; }
60
- public void setAverageRating(double averageRating) { this.averageRating = averageRating; }
61
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/dto/response/JwtAuthResponseDto.java DELETED
@@ -1,43 +0,0 @@
1
- package com.locallend.locallend.dto.response;
2
-
3
- /**
4
- * DTO for JWT authentication response.
5
- * Contains the JWT token and user information.
6
- */
7
- public class JwtAuthResponseDto {
8
- private String token;
9
- private String tokenType = "Bearer";
10
- private UserResponseDto user;
11
-
12
- public JwtAuthResponseDto() {}
13
-
14
- public JwtAuthResponseDto(String token, UserResponseDto user) {
15
- this.token = token;
16
- this.user = user;
17
- }
18
-
19
- // Getters and Setters
20
- public String getToken() {
21
- return token;
22
- }
23
-
24
- public void setToken(String token) {
25
- this.token = token;
26
- }
27
-
28
- public String getTokenType() {
29
- return tokenType;
30
- }
31
-
32
- public void setTokenType(String tokenType) {
33
- this.tokenType = tokenType;
34
- }
35
-
36
- public UserResponseDto getUser() {
37
- return user;
38
- }
39
-
40
- public void setUser(UserResponseDto user) {
41
- this.user = user;
42
- }
43
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/dto/response/RatingResponseDto.java DELETED
@@ -1,89 +0,0 @@
1
- package com.locallend.locallend.dto.response;
2
-
3
- import com.locallend.locallend.model.RatingType;
4
- import java.time.LocalDateTime;
5
-
6
- /**
7
- * Response DTO for Rating entity.
8
- * Includes computed fields (timeAgo, canEdit) and optional user/item names.
9
- * Issue #23: Rating Entity, Repository, DTO
10
- */
11
- public class RatingResponseDto {
12
-
13
- private String id;
14
- private String raterId;
15
- private String raterName;
16
- private String rateeId;
17
- private String rateeName;
18
- private String itemId;
19
- private String itemName;
20
- private String bookingId;
21
- private RatingType ratingType;
22
- private Integer ratingValue;
23
- private String comment;
24
- private LocalDateTime createdDate;
25
- private LocalDateTime updatedDate;
26
- private Boolean isAnonymous;
27
- private Integer helpfulCount;
28
- private Boolean isVerified;
29
- private String timeAgo;
30
- private Boolean canEdit;
31
-
32
- // Constructors
33
- public RatingResponseDto() {}
34
-
35
- // Getters and Setters
36
- public String getId() { return id; }
37
- public void setId(String id) { this.id = id; }
38
-
39
- public String getRaterId() { return raterId; }
40
- public void setRaterId(String raterId) { this.raterId = raterId; }
41
-
42
- public String getRaterName() { return raterName; }
43
- public void setRaterName(String raterName) { this.raterName = raterName; }
44
-
45
- public String getRateeId() { return rateeId; }
46
- public void setRateeId(String rateeId) { this.rateeId = rateeId; }
47
-
48
- public String getRateeName() { return rateeName; }
49
- public void setRateeName(String rateeName) { this.rateeName = rateeName; }
50
-
51
- public String getItemId() { return itemId; }
52
- public void setItemId(String itemId) { this.itemId = itemId; }
53
-
54
- public String getItemName() { return itemName; }
55
- public void setItemName(String itemName) { this.itemName = itemName; }
56
-
57
- public String getBookingId() { return bookingId; }
58
- public void setBookingId(String bookingId) { this.bookingId = bookingId; }
59
-
60
- public RatingType getRatingType() { return ratingType; }
61
- public void setRatingType(RatingType ratingType) { this.ratingType = ratingType; }
62
-
63
- public Integer getRatingValue() { return ratingValue; }
64
- public void setRatingValue(Integer ratingValue) { this.ratingValue = ratingValue; }
65
-
66
- public String getComment() { return comment; }
67
- public void setComment(String comment) { this.comment = comment; }
68
-
69
- public LocalDateTime getCreatedDate() { return createdDate; }
70
- public void setCreatedDate(LocalDateTime createdDate) { this.createdDate = createdDate; }
71
-
72
- public LocalDateTime getUpdatedDate() { return updatedDate; }
73
- public void setUpdatedDate(LocalDateTime updatedDate) { this.updatedDate = updatedDate; }
74
-
75
- public Boolean getIsAnonymous() { return isAnonymous; }
76
- public void setIsAnonymous(Boolean isAnonymous) { this.isAnonymous = isAnonymous; }
77
-
78
- public Integer getHelpfulCount() { return helpfulCount; }
79
- public void setHelpfulCount(Integer helpfulCount) { this.helpfulCount = helpfulCount; }
80
-
81
- public Boolean getIsVerified() { return isVerified; }
82
- public void setIsVerified(Boolean isVerified) { this.isVerified = isVerified; }
83
-
84
- public String getTimeAgo() { return timeAgo; }
85
- public void setTimeAgo(String timeAgo) { this.timeAgo = timeAgo; }
86
-
87
- public Boolean getCanEdit() { return canEdit; }
88
- public void setCanEdit(Boolean canEdit) { this.canEdit = canEdit; }
89
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/dto/response/RatingStatsResponse.java DELETED
@@ -1,74 +0,0 @@
1
- package com.locallend.locallend.dto.response;
2
-
3
- import com.fasterxml.jackson.annotation.JsonProperty;
4
- import java.util.Map;
5
-
6
- /**
7
- * Response DTO for rating statistics.
8
- * Provides aggregated rating data for users or items.
9
- * Issue #25: Rating Controller APIs
10
- */
11
- public class RatingStatsResponse {
12
-
13
- @JsonProperty("subject_id")
14
- private String subjectId; // User ID or Item ID
15
-
16
- @JsonProperty("subject_type")
17
- private String subjectType; // "USER" or "ITEM"
18
-
19
- @JsonProperty("average_rating")
20
- private Double averageRating;
21
-
22
- @JsonProperty("total_ratings")
23
- private Long totalRatings;
24
-
25
- @JsonProperty("rating_distribution")
26
- private Map<String, Long> ratingDistribution; // "5" -> count, "4" -> count, etc.
27
-
28
- @JsonProperty("recent_average")
29
- private Double recentAverage; // Last 30 days
30
-
31
- @JsonProperty("verified_ratings_count")
32
- private Long verifiedRatingsCount;
33
-
34
- // Constructors
35
- public RatingStatsResponse() {}
36
-
37
- public RatingStatsResponse(String subjectId, String subjectType, Double averageRating, Long totalRatings) {
38
- this.subjectId = subjectId;
39
- this.subjectType = subjectType;
40
- this.averageRating = averageRating;
41
- this.totalRatings = totalRatings;
42
- }
43
-
44
- // Getters and Setters
45
- public String getSubjectId() { return subjectId; }
46
- public void setSubjectId(String subjectId) { this.subjectId = subjectId; }
47
-
48
- public String getSubjectType() { return subjectType; }
49
- public void setSubjectType(String subjectType) { this.subjectType = subjectType; }
50
-
51
- public Double getAverageRating() { return averageRating; }
52
- public void setAverageRating(Double averageRating) { this.averageRating = averageRating; }
53
-
54
- public Long getTotalRatings() { return totalRatings; }
55
- public void setTotalRatings(Long totalRatings) { this.totalRatings = totalRatings; }
56
-
57
- public Map<String, Long> getRatingDistribution() { return ratingDistribution; }
58
- public void setRatingDistribution(Map<String, Long> ratingDistribution) { this.ratingDistribution = ratingDistribution; }
59
-
60
- public Double getRecentAverage() { return recentAverage; }
61
- public void setRecentAverage(Double recentAverage) { this.recentAverage = recentAverage; }
62
-
63
- public Long getVerifiedRatingsCount() { return verifiedRatingsCount; }
64
- public void setVerifiedRatingsCount(Long verifiedRatingsCount) { this.verifiedRatingsCount = verifiedRatingsCount; }
65
-
66
- // Helper methods
67
- public String getFormattedAverageRating() {
68
- return averageRating != null ? String.format("%.1f", averageRating) : "N/A";
69
- }
70
-
71
- public boolean hasRatings() {
72
- return totalRatings != null && totalRatings > 0;
73
- }
74
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/dto/response/UserPublicDto.java DELETED
@@ -1,76 +0,0 @@
1
- package com.locallend.locallend.dto.response;
2
-
3
- /**
4
- * Public user information DTO.
5
- * Contains only publicly visible user information (no email, phone, etc.).
6
- */
7
- public class UserPublicDto {
8
- private String id;
9
- private String username;
10
- private String name;
11
- private String profileImageUrl;
12
- private Double trustScore;
13
- private Long itemCount;
14
-
15
- // Computed field for Issue #29
16
- private String memberSince; // "Member since MMMM yyyy"
17
-
18
- public UserPublicDto() {}
19
-
20
- // Getters and Setters
21
- public String getId() {
22
- return id;
23
- }
24
-
25
- public void setId(String id) {
26
- this.id = id;
27
- }
28
-
29
- public String getUsername() {
30
- return username;
31
- }
32
-
33
- public void setUsername(String username) {
34
- this.username = username;
35
- }
36
-
37
- public String getName() {
38
- return name;
39
- }
40
-
41
- public void setName(String name) {
42
- this.name = name;
43
- }
44
-
45
- public String getProfileImageUrl() {
46
- return profileImageUrl;
47
- }
48
-
49
- public void setProfileImageUrl(String profileImageUrl) {
50
- this.profileImageUrl = profileImageUrl;
51
- }
52
-
53
- public Double getTrustScore() {
54
- return trustScore;
55
- }
56
-
57
- public void setTrustScore(Double trustScore) {
58
- this.trustScore = trustScore;
59
- }
60
-
61
- public Long getItemCount() {
62
- return itemCount;
63
- }
64
-
65
- public void setItemCount(Long itemCount) {
66
- this.itemCount = itemCount;
67
- }
68
-
69
- public String getMemberSince() {
70
- return memberSince;
71
- }
72
-
73
- public void setMemberSince(String memberSince) {
74
- this.memberSince = memberSince;
75
- }
76
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/dto/response/UserResponseDto.java DELETED
@@ -1,141 +0,0 @@
1
- package com.locallend.locallend.dto.response;
2
-
3
- import java.time.LocalDateTime;
4
-
5
- /**
6
- * Full user response DTO for authenticated user operations.
7
- * Contains all non-sensitive user information.
8
- */
9
- public class UserResponseDto {
10
- private String id;
11
- private String username;
12
- private String name;
13
- private String email;
14
- private String phoneNumber;
15
- private String profileImageUrl;
16
- private String role;
17
- private Boolean isActive;
18
- private LocalDateTime createdDate;
19
- private Double trustScore;
20
- private Long itemCount;
21
-
22
- // Computed fields for Issue #29
23
- private String memberSince; // "Member since MMMM yyyy"
24
- private Integer totalItemsShared; // Total items created by user
25
- private Integer totalBookings; // Total bookings as owner or borrower
26
-
27
- public UserResponseDto() {}
28
-
29
- // Getters and Setters
30
- public String getId() {
31
- return id;
32
- }
33
-
34
- public void setId(String id) {
35
- this.id = id;
36
- }
37
-
38
- public String getUsername() {
39
- return username;
40
- }
41
-
42
- public void setUsername(String username) {
43
- this.username = username;
44
- }
45
-
46
- public String getName() {
47
- return name;
48
- }
49
-
50
- public void setName(String name) {
51
- this.name = name;
52
- }
53
-
54
- public String getEmail() {
55
- return email;
56
- }
57
-
58
- public void setEmail(String email) {
59
- this.email = email;
60
- }
61
-
62
- public String getPhoneNumber() {
63
- return phoneNumber;
64
- }
65
-
66
- public void setPhoneNumber(String phoneNumber) {
67
- this.phoneNumber = phoneNumber;
68
- }
69
-
70
- public String getProfileImageUrl() {
71
- return profileImageUrl;
72
- }
73
-
74
- public void setProfileImageUrl(String profileImageUrl) {
75
- this.profileImageUrl = profileImageUrl;
76
- }
77
-
78
- public String getRole() {
79
- return role;
80
- }
81
-
82
- public void setRole(String role) {
83
- this.role = role;
84
- }
85
-
86
- public Boolean getIsActive() {
87
- return isActive;
88
- }
89
-
90
- public void setIsActive(Boolean isActive) {
91
- this.isActive = isActive;
92
- }
93
-
94
- public LocalDateTime getCreatedDate() {
95
- return createdDate;
96
- }
97
-
98
- public void setCreatedDate(LocalDateTime createdDate) {
99
- this.createdDate = createdDate;
100
- }
101
-
102
- public Double getTrustScore() {
103
- return trustScore;
104
- }
105
-
106
- public void setTrustScore(Double trustScore) {
107
- this.trustScore = trustScore;
108
- }
109
-
110
- public Long getItemCount() {
111
- return itemCount;
112
- }
113
-
114
- public void setItemCount(Long itemCount) {
115
- this.itemCount = itemCount;
116
- }
117
-
118
- public String getMemberSince() {
119
- return memberSince;
120
- }
121
-
122
- public void setMemberSince(String memberSince) {
123
- this.memberSince = memberSince;
124
- }
125
-
126
- public Integer getTotalItemsShared() {
127
- return totalItemsShared;
128
- }
129
-
130
- public void setTotalItemsShared(Integer totalItemsShared) {
131
- this.totalItemsShared = totalItemsShared;
132
- }
133
-
134
- public Integer getTotalBookings() {
135
- return totalBookings;
136
- }
137
-
138
- public void setTotalBookings(Integer totalBookings) {
139
- this.totalBookings = totalBookings;
140
- }
141
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/exception/BookingConflictException.java DELETED
@@ -1,37 +0,0 @@
1
- package com.locallend.locallend.exception;
2
-
3
- /**
4
- * Exception thrown when a booking request conflicts with existing bookings or availability constraints.
5
- */
6
- public class BookingConflictException extends RuntimeException {
7
-
8
- private String itemId;
9
- private String conflictingBookingId;
10
-
11
- public BookingConflictException(String message) {
12
- super(message);
13
- }
14
-
15
- public BookingConflictException(String message, String itemId) {
16
- super(message);
17
- this.itemId = itemId;
18
- }
19
-
20
- public BookingConflictException(String message, String itemId, String conflictingBookingId) {
21
- super(message);
22
- this.itemId = itemId;
23
- this.conflictingBookingId = conflictingBookingId;
24
- }
25
-
26
- public BookingConflictException(String message, Throwable cause) {
27
- super(message, cause);
28
- }
29
-
30
- public String getItemId() {
31
- return itemId;
32
- }
33
-
34
- public String getConflictingBookingId() {
35
- return conflictingBookingId;
36
- }
37
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/exception/BookingNotFoundException.java DELETED
@@ -1,32 +0,0 @@
1
- package com.locallend.locallend.exception;
2
-
3
- /**
4
- * Exception thrown when a booking is not found by ID.
5
- */
6
- public class BookingNotFoundException extends RuntimeException {
7
-
8
- private String bookingId;
9
-
10
- public BookingNotFoundException(String bookingId) {
11
- super("Booking not found with ID: " + bookingId);
12
- this.bookingId = bookingId;
13
- }
14
-
15
- public BookingNotFoundException(String bookingId, String message) {
16
- super(message);
17
- this.bookingId = bookingId;
18
- }
19
-
20
- public BookingNotFoundException(String bookingId, String message, Throwable cause) {
21
- super(message, cause);
22
- this.bookingId = bookingId;
23
- }
24
-
25
- public String getBookingId() {
26
- return bookingId;
27
- }
28
-
29
- public static BookingNotFoundException withId(String bookingId) {
30
- return new BookingNotFoundException(bookingId);
31
- }
32
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/exception/BusinessException.java DELETED
@@ -1,11 +0,0 @@
1
- package com.locallend.locallend.exception;
2
-
3
- /**
4
- * Exception thrown when business rules are violated.
5
- * Examples: duplicate user, inactive user, invalid trust score, etc.
6
- */
7
- public class BusinessException extends RuntimeException {
8
- public BusinessException(String message) {
9
- super(message);
10
- }
11
- }
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/exception/CategoryNotFoundException.java DELETED
@@ -1,36 +0,0 @@
1
- package com.locallend.locallend.exception;
2
-
3
- public class CategoryNotFoundException extends RuntimeException {
4
-
5
- private final String categoryId;
6
- private final String categoryName;
7
-
8
- public CategoryNotFoundException(String categoryId) {
9
- super("Category with ID '" + categoryId + "' not found");
10
- this.categoryId = categoryId;
11
- this.categoryName = null;
12
- }
13
-
14
- public CategoryNotFoundException(String categoryName, boolean byName) {
15
- super("Category with name '" + categoryName + "' not found");
16
- this.categoryId = null;
17
- this.categoryName = categoryName;
18
- }
19
-
20
- public CategoryNotFoundException(String message, String categoryId, String categoryName) {
21
- super(message);
22
- this.categoryId = categoryId;
23
- this.categoryName = categoryName;
24
- }
25
-
26
- public static CategoryNotFoundException byId(String categoryId) {
27
- return new CategoryNotFoundException(categoryId);
28
- }
29
-
30
- public static CategoryNotFoundException byName(String categoryName) {
31
- return new CategoryNotFoundException(categoryName, true);
32
- }
33
-
34
- public String getCategoryId() { return categoryId; }
35
- public String getCategoryName() { return categoryName; }
36
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/exception/DuplicateResourceException.java DELETED
@@ -1,37 +0,0 @@
1
- package com.locallend.locallend.exception;
2
-
3
- /**
4
- * Exception thrown when attempting to create a resource that already exists.
5
- * Examples: duplicate username, duplicate email, duplicate category name.
6
- */
7
- public class DuplicateResourceException extends RuntimeException {
8
- private final String resourceType;
9
- private final String fieldName;
10
- private final String fieldValue;
11
-
12
- public DuplicateResourceException(String resourceType, String fieldName, String fieldValue) {
13
- super(String.format("%s with %s '%s' already exists", resourceType, fieldName, fieldValue));
14
- this.resourceType = resourceType;
15
- this.fieldName = fieldName;
16
- this.fieldValue = fieldValue;
17
- }
18
-
19
- public DuplicateResourceException(String message) {
20
- super(message);
21
- this.resourceType = null;
22
- this.fieldName = null;
23
- this.fieldValue = null;
24
- }
25
-
26
- public String getResourceType() {
27
- return resourceType;
28
- }
29
-
30
- public String getFieldName() {
31
- return fieldName;
32
- }
33
-
34
- public String getFieldValue() {
35
- return fieldValue;
36
- }
37
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/exception/GlobalExceptionHandler.java DELETED
@@ -1,504 +0,0 @@
1
- package com.locallend.locallend.exception;
2
-
3
- import org.springframework.http.HttpStatus;
4
- import org.springframework.http.ResponseEntity;
5
- import org.springframework.http.converter.HttpMessageNotReadableException;
6
- import org.springframework.security.access.AccessDeniedException;
7
- import org.springframework.security.authentication.BadCredentialsException;
8
- import org.springframework.security.core.AuthenticationException;
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.MissingServletRequestParameterException;
13
- import org.springframework.web.bind.annotation.ControllerAdvice;
14
- import org.springframework.web.bind.annotation.ExceptionHandler;
15
- import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
16
- import org.springframework.web.servlet.NoHandlerFoundException;
17
-
18
- import java.time.LocalDateTime;
19
- import java.util.HashMap;
20
- import java.util.Map;
21
-
22
- /**
23
- * Global exception handler for all controllers.
24
- * Converts exceptions to standardized error responses.
25
- */
26
- @ControllerAdvice
27
- public class GlobalExceptionHandler {
28
-
29
- /**
30
- * Handle BusinessException - domain/business logic violations.
31
- * @param ex BusinessException
32
- * @return Error response with 400 BAD REQUEST
33
- */
34
- @ExceptionHandler(BusinessException.class)
35
- public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex) {
36
- ErrorResponse error = new ErrorResponse(
37
- HttpStatus.BAD_REQUEST.value(),
38
- ex.getMessage(),
39
- LocalDateTime.now()
40
- );
41
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
42
- }
43
-
44
- /**
45
- * Handle validation errors from @Valid annotation.
46
- * @param ex MethodArgumentNotValidException
47
- * @return Error response with 400 BAD REQUEST and field errors
48
- */
49
- @ExceptionHandler(MethodArgumentNotValidException.class)
50
- public ResponseEntity<ValidationErrorResponse> handleValidationException(MethodArgumentNotValidException ex) {
51
- Map<String, String> fieldErrors = new HashMap<>();
52
-
53
- ex.getBindingResult().getAllErrors().forEach((error) -> {
54
- String fieldName = ((FieldError) error).getField();
55
- String errorMessage = error.getDefaultMessage();
56
- fieldErrors.put(fieldName, errorMessage);
57
- });
58
-
59
- ValidationErrorResponse error = new ValidationErrorResponse(
60
- HttpStatus.BAD_REQUEST.value(),
61
- "Validation failed",
62
- fieldErrors,
63
- LocalDateTime.now()
64
- );
65
-
66
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
67
- }
68
-
69
- /**
70
- * Handle custom ValidationException - for domain-specific validation errors.
71
- * @param ex ValidationException
72
- * @return Error response with 400 BAD REQUEST and field errors
73
- */
74
- @ExceptionHandler(ValidationException.class)
75
- public ResponseEntity<ValidationErrorResponse> handleCustomValidationException(ValidationException ex) {
76
- ValidationErrorResponse error = new ValidationErrorResponse(
77
- HttpStatus.BAD_REQUEST.value(),
78
- ex.getMessage(),
79
- ex.getFieldErrors(),
80
- LocalDateTime.now()
81
- );
82
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
83
- }
84
-
85
- /**
86
- * Handle DuplicateResourceException - when trying to create duplicate resource.
87
- * @param ex DuplicateResourceException
88
- * @return Error response with 409 CONFLICT
89
- */
90
- @ExceptionHandler(DuplicateResourceException.class)
91
- public ResponseEntity<ErrorResponse> handleDuplicateResourceException(DuplicateResourceException ex) {
92
- ErrorResponse error = new ErrorResponse(
93
- HttpStatus.CONFLICT.value(),
94
- ex.getMessage(),
95
- LocalDateTime.now()
96
- );
97
- return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
98
- }
99
-
100
- /**
101
- * Handle InvalidOperationException - when operation cannot be performed.
102
- * @param ex InvalidOperationException
103
- * @return Error response with 400 BAD REQUEST
104
- */
105
- @ExceptionHandler(InvalidOperationException.class)
106
- public ResponseEntity<ErrorResponse> handleInvalidOperationException(InvalidOperationException ex) {
107
- ErrorResponse error = new ErrorResponse(
108
- HttpStatus.BAD_REQUEST.value(),
109
- ex.getMessage(),
110
- LocalDateTime.now()
111
- );
112
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
113
- }
114
-
115
- /**
116
- * Handle InvalidStateTransitionException - when state transition is not allowed.
117
- * @param ex InvalidStateTransitionException
118
- * @return Error response with 400 BAD REQUEST
119
- */
120
- @ExceptionHandler(InvalidStateTransitionException.class)
121
- public ResponseEntity<ErrorResponse> handleInvalidStateTransitionException(InvalidStateTransitionException ex) {
122
- ErrorResponse error = new ErrorResponse(
123
- HttpStatus.BAD_REQUEST.value(),
124
- ex.getMessage(),
125
- LocalDateTime.now()
126
- );
127
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
128
- }
129
-
130
- /**
131
- * Handle InsufficientTrustScoreException - when user's trust score is too low.
132
- * @param ex InsufficientTrustScoreException
133
- * @return Error response with 403 FORBIDDEN
134
- */
135
- @ExceptionHandler(InsufficientTrustScoreException.class)
136
- public ResponseEntity<ErrorResponse> handleInsufficientTrustScoreException(InsufficientTrustScoreException ex) {
137
- ErrorResponse error = new ErrorResponse(
138
- HttpStatus.FORBIDDEN.value(),
139
- ex.getMessage(),
140
- LocalDateTime.now()
141
- );
142
- return ResponseEntity.status(HttpStatus.FORBIDDEN).body(error);
143
- }
144
-
145
- /**
146
- * Handle ItemNotAvailableException - when item is not available for booking.
147
- * @param ex ItemNotAvailableException
148
- * @return Error response with 409 CONFLICT
149
- */
150
- @ExceptionHandler(ItemNotAvailableException.class)
151
- public ResponseEntity<ErrorResponse> handleItemNotAvailableException(ItemNotAvailableException ex) {
152
- ErrorResponse error = new ErrorResponse(
153
- HttpStatus.CONFLICT.value(),
154
- ex.getMessage(),
155
- LocalDateTime.now()
156
- );
157
- return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
158
- }
159
-
160
- /**
161
- * Handle BookingConflictException - when booking dates conflict.
162
- * @param ex BookingConflictException
163
- * @return Error response with 409 CONFLICT
164
- */
165
- @ExceptionHandler(BookingConflictException.class)
166
- public ResponseEntity<ErrorResponse> handleBookingConflictException(BookingConflictException ex) {
167
- ErrorResponse error = new ErrorResponse(
168
- HttpStatus.CONFLICT.value(),
169
- ex.getMessage(),
170
- LocalDateTime.now()
171
- );
172
- return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
173
- }
174
-
175
- /**
176
- * Handle InvalidBookingPeriodException - when booking period is invalid.
177
- * @param ex InvalidBookingPeriodException
178
- * @return Error response with 400 BAD REQUEST
179
- */
180
- @ExceptionHandler(InvalidBookingPeriodException.class)
181
- public ResponseEntity<ErrorResponse> handleInvalidBookingPeriodException(InvalidBookingPeriodException ex) {
182
- ErrorResponse error = new ErrorResponse(
183
- HttpStatus.BAD_REQUEST.value(),
184
- ex.getMessage(),
185
- LocalDateTime.now()
186
- );
187
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
188
- }
189
-
190
- /**
191
- * Handle ResourceNotFoundException - generic resource not found.
192
- * @param ex ResourceNotFoundException
193
- * @return Error response with 404 NOT FOUND
194
- */
195
- @ExceptionHandler(ResourceNotFoundException.class)
196
- public ResponseEntity<ErrorResponse> handleResourceNotFoundException(ResourceNotFoundException ex) {
197
- ErrorResponse error = new ErrorResponse(
198
- HttpStatus.NOT_FOUND.value(),
199
- ex.getMessage(),
200
- LocalDateTime.now()
201
- );
202
- return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
203
- }
204
-
205
- /**
206
- * Handle CategoryNotFoundException - when category is not found.
207
- * @param ex CategoryNotFoundException
208
- * @return Error response with 404 NOT FOUND
209
- */
210
- @ExceptionHandler(CategoryNotFoundException.class)
211
- public ResponseEntity<ErrorResponse> handleCategoryNotFoundException(CategoryNotFoundException ex) {
212
- ErrorResponse error = new ErrorResponse(
213
- HttpStatus.NOT_FOUND.value(),
214
- ex.getMessage(),
215
- LocalDateTime.now()
216
- );
217
- return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
218
- }
219
-
220
- /**
221
- * Handle ItemNotFoundException - when item is not found.
222
- * @param ex ItemNotFoundException
223
- * @return Error response with 404 NOT FOUND
224
- */
225
- @ExceptionHandler(ItemNotFoundException.class)
226
- public ResponseEntity<ErrorResponse> handleItemNotFoundException(ItemNotFoundException ex) {
227
- ErrorResponse error = new ErrorResponse(
228
- HttpStatus.NOT_FOUND.value(),
229
- ex.getMessage(),
230
- LocalDateTime.now()
231
- );
232
- return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
233
- }
234
-
235
- /**
236
- * Handle UserNotFoundException - when user is not found.
237
- * @param ex UserNotFoundException
238
- * @return Error response with 404 NOT FOUND
239
- */
240
- @ExceptionHandler(UserNotFoundException.class)
241
- public ResponseEntity<ErrorResponse> handleUserNotFoundException(UserNotFoundException ex) {
242
- ErrorResponse error = new ErrorResponse(
243
- HttpStatus.NOT_FOUND.value(),
244
- ex.getMessage(),
245
- LocalDateTime.now()
246
- );
247
- return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
248
- }
249
-
250
- /**
251
- * Handle BookingNotFoundException - when booking is not found.
252
- * @param ex BookingNotFoundException
253
- * @return Error response with 404 NOT FOUND
254
- */
255
- @ExceptionHandler(BookingNotFoundException.class)
256
- public ResponseEntity<ErrorResponse> handleBookingNotFoundException(BookingNotFoundException ex) {
257
- ErrorResponse error = new ErrorResponse(
258
- HttpStatus.NOT_FOUND.value(),
259
- ex.getMessage(),
260
- LocalDateTime.now()
261
- );
262
- return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
263
- }
264
-
265
- /**
266
- * Handle RatingNotFoundException - when rating is not found.
267
- * @param ex RatingNotFoundException
268
- * @return Error response with 404 NOT FOUND
269
- */
270
- @ExceptionHandler(RatingNotFoundException.class)
271
- public ResponseEntity<ErrorResponse> handleRatingNotFoundException(RatingNotFoundException ex) {
272
- ErrorResponse error = new ErrorResponse(
273
- HttpStatus.NOT_FOUND.value(),
274
- ex.getMessage(),
275
- LocalDateTime.now()
276
- );
277
- return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
278
- }
279
-
280
- /**
281
- * Handle UnauthorizedItemAccessException - when user tries to access/modify item they don't own.
282
- * @param ex UnauthorizedItemAccessException
283
- * @return Error response with 403 FORBIDDEN
284
- */
285
- @ExceptionHandler(UnauthorizedItemAccessException.class)
286
- public ResponseEntity<ErrorResponse> handleUnauthorizedItemAccessException(UnauthorizedItemAccessException ex) {
287
- ErrorResponse error = new ErrorResponse(
288
- HttpStatus.FORBIDDEN.value(),
289
- ex.getMessage(),
290
- LocalDateTime.now()
291
- );
292
- return ResponseEntity.status(HttpStatus.FORBIDDEN).body(error);
293
- }
294
-
295
- /**
296
- * Handle UnauthorizedAccessException - generic unauthorized access.
297
- * @param ex UnauthorizedAccessException
298
- * @return Error response with 403 FORBIDDEN
299
- */
300
- @ExceptionHandler(UnauthorizedAccessException.class)
301
- public ResponseEntity<ErrorResponse> handleUnauthorizedAccessException(UnauthorizedAccessException ex) {
302
- ErrorResponse error = new ErrorResponse(
303
- HttpStatus.FORBIDDEN.value(),
304
- ex.getMessage(),
305
- LocalDateTime.now()
306
- );
307
- return ResponseEntity.status(HttpStatus.FORBIDDEN).body(error);
308
- }
309
-
310
- /**
311
- * Handle Spring Security AuthenticationException - authentication failures.
312
- * @param ex AuthenticationException
313
- * @return Error response with 401 UNAUTHORIZED
314
- */
315
- @ExceptionHandler(AuthenticationException.class)
316
- public ResponseEntity<ErrorResponse> handleAuthenticationException(AuthenticationException ex) {
317
- ErrorResponse error = new ErrorResponse(
318
- HttpStatus.UNAUTHORIZED.value(),
319
- "Authentication failed: " + ex.getMessage(),
320
- LocalDateTime.now()
321
- );
322
- return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(error);
323
- }
324
-
325
- /**
326
- * Handle Spring Security BadCredentialsException - invalid username/password.
327
- * @param ex BadCredentialsException
328
- * @return Error response with 401 UNAUTHORIZED
329
- */
330
- @ExceptionHandler(BadCredentialsException.class)
331
- public ResponseEntity<ErrorResponse> handleBadCredentialsException(BadCredentialsException ex) {
332
- ErrorResponse error = new ErrorResponse(
333
- HttpStatus.UNAUTHORIZED.value(),
334
- "Invalid username or password",
335
- LocalDateTime.now()
336
- );
337
- return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(error);
338
- }
339
-
340
- /**
341
- * Handle Spring Security AccessDeniedException - insufficient permissions.
342
- * @param ex AccessDeniedException
343
- * @return Error response with 403 FORBIDDEN
344
- */
345
- @ExceptionHandler(AccessDeniedException.class)
346
- public ResponseEntity<ErrorResponse> handleAccessDeniedException(AccessDeniedException ex) {
347
- ErrorResponse error = new ErrorResponse(
348
- HttpStatus.FORBIDDEN.value(),
349
- "Access denied: You don't have permission to access this resource",
350
- LocalDateTime.now()
351
- );
352
- return ResponseEntity.status(HttpStatus.FORBIDDEN).body(error);
353
- }
354
-
355
- /**
356
- * Handle HttpMessageNotReadableException - malformed JSON request body.
357
- * @param ex HttpMessageNotReadableException
358
- * @return Error response with 400 BAD REQUEST
359
- */
360
- @ExceptionHandler(HttpMessageNotReadableException.class)
361
- public ResponseEntity<ErrorResponse> handleHttpMessageNotReadableException(HttpMessageNotReadableException ex) {
362
- ErrorResponse error = new ErrorResponse(
363
- HttpStatus.BAD_REQUEST.value(),
364
- "Malformed JSON request: " + ex.getMessage(),
365
- LocalDateTime.now()
366
- );
367
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
368
- }
369
-
370
- /**
371
- * Handle MethodArgumentTypeMismatchException - wrong parameter type.
372
- * @param ex MethodArgumentTypeMismatchException
373
- * @return Error response with 400 BAD REQUEST
374
- */
375
- @ExceptionHandler(MethodArgumentTypeMismatchException.class)
376
- public ResponseEntity<ErrorResponse> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException ex) {
377
- String message = String.format("Parameter '%s' should be of type %s",
378
- ex.getName(),
379
- ex.getRequiredType() != null ? ex.getRequiredType().getSimpleName() : "unknown");
380
-
381
- ErrorResponse error = new ErrorResponse(
382
- HttpStatus.BAD_REQUEST.value(),
383
- message,
384
- LocalDateTime.now()
385
- );
386
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
387
- }
388
-
389
- /**
390
- * Handle MissingServletRequestParameterException - missing required parameter.
391
- * @param ex MissingServletRequestParameterException
392
- * @return Error response with 400 BAD REQUEST
393
- */
394
- @ExceptionHandler(MissingServletRequestParameterException.class)
395
- public ResponseEntity<ErrorResponse> handleMissingServletRequestParameterException(MissingServletRequestParameterException ex) {
396
- String message = String.format("Required parameter '%s' is missing", ex.getParameterName());
397
-
398
- ErrorResponse error = new ErrorResponse(
399
- HttpStatus.BAD_REQUEST.value(),
400
- message,
401
- LocalDateTime.now()
402
- );
403
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
404
- }
405
-
406
- /**
407
- * Handle HttpRequestMethodNotSupportedException - unsupported HTTP method.
408
- * @param ex HttpRequestMethodNotSupportedException
409
- * @return Error response with 405 METHOD NOT ALLOWED
410
- */
411
- @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
412
- public ResponseEntity<ErrorResponse> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException ex) {
413
- String message = String.format("Method '%s' is not supported for this endpoint", ex.getMethod());
414
-
415
- ErrorResponse error = new ErrorResponse(
416
- HttpStatus.METHOD_NOT_ALLOWED.value(),
417
- message,
418
- LocalDateTime.now()
419
- );
420
- return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).body(error);
421
- }
422
-
423
- /**
424
- * Handle NoHandlerFoundException - endpoint not found.
425
- * @param ex NoHandlerFoundException
426
- * @return Error response with 404 NOT FOUND
427
- */
428
- @ExceptionHandler(NoHandlerFoundException.class)
429
- public ResponseEntity<ErrorResponse> handleNoHandlerFoundException(NoHandlerFoundException ex) {
430
- String message = String.format("Endpoint '%s' not found", ex.getRequestURL());
431
-
432
- ErrorResponse error = new ErrorResponse(
433
- HttpStatus.NOT_FOUND.value(),
434
- message,
435
- LocalDateTime.now()
436
- );
437
- return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
438
- }
439
-
440
- /**
441
- * Handle IllegalArgumentException - invalid arguments to methods.
442
- * @param ex IllegalArgumentException
443
- * @return Error response with 400 BAD REQUEST
444
- */
445
- @ExceptionHandler(IllegalArgumentException.class)
446
- public ResponseEntity<ErrorResponse> handleIllegalArgumentException(IllegalArgumentException ex) {
447
- ErrorResponse error = new ErrorResponse(
448
- HttpStatus.BAD_REQUEST.value(),
449
- "Invalid argument: " + ex.getMessage(),
450
- LocalDateTime.now()
451
- );
452
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
453
- }
454
-
455
- /**
456
- * Handle generic exceptions.
457
- * @param ex Exception
458
- * @return Error response with 500 INTERNAL SERVER ERROR
459
- */
460
- @ExceptionHandler(Exception.class)
461
- public ResponseEntity<ErrorResponse> handleGenericException(Exception ex) {
462
- ErrorResponse error = new ErrorResponse(
463
- HttpStatus.INTERNAL_SERVER_ERROR.value(),
464
- "An unexpected error occurred: " + ex.getMessage(),
465
- LocalDateTime.now()
466
- );
467
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
468
- }
469
-
470
- /**
471
- * Standard error response structure.
472
- */
473
- public static class ErrorResponse {
474
- private int status;
475
- private String message;
476
- private LocalDateTime timestamp;
477
-
478
- public ErrorResponse(int status, String message, LocalDateTime timestamp) {
479
- this.status = status;
480
- this.message = message;
481
- this.timestamp = timestamp;
482
- }
483
-
484
- // Getters
485
- public int getStatus() { return status; }
486
- public String getMessage() { return message; }
487
- public LocalDateTime getTimestamp() { return timestamp; }
488
- }
489
-
490
- /**
491
- * Validation error response with field-level errors.
492
- */
493
- public static class ValidationErrorResponse extends ErrorResponse {
494
- private Map<String, String> fieldErrors;
495
-
496
- public ValidationErrorResponse(int status, String message, Map<String, String> fieldErrors, LocalDateTime timestamp) {
497
- super(status, message, timestamp);
498
- this.fieldErrors = fieldErrors;
499
- }
500
-
501
- // Getter
502
- public Map<String, String> getFieldErrors() { return fieldErrors; }
503
- }
504
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/exception/InsufficientTrustScoreException.java DELETED
@@ -1,31 +0,0 @@
1
- package com.locallend.locallend.exception;
2
-
3
- /**
4
- * Exception thrown when a user's trust score is too low to perform an operation.
5
- * Example: Cannot create booking with trust score below threshold.
6
- */
7
- public class InsufficientTrustScoreException extends RuntimeException {
8
- private final String userId;
9
- private final Double currentScore;
10
- private final Double requiredScore;
11
-
12
- public InsufficientTrustScoreException(String userId, Double currentScore, Double requiredScore) {
13
- super(String.format("User '%s' has insufficient trust score. Current: %.2f, Required: %.2f",
14
- userId, currentScore, requiredScore));
15
- this.userId = userId;
16
- this.currentScore = currentScore;
17
- this.requiredScore = requiredScore;
18
- }
19
-
20
- public String getUserId() {
21
- return userId;
22
- }
23
-
24
- public Double getCurrentScore() {
25
- return currentScore;
26
- }
27
-
28
- public Double getRequiredScore() {
29
- return requiredScore;
30
- }
31
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/exception/InvalidBookingPeriodException.java DELETED
@@ -1,38 +0,0 @@
1
- package com.locallend.locallend.exception;
2
-
3
- /**
4
- * Exception thrown when a booking period violates rental period constraints.
5
- */
6
- public class InvalidBookingPeriodException extends RuntimeException {
7
-
8
- private Integer minDays;
9
- private Integer maxDays;
10
- private Long requestedDays;
11
-
12
- public InvalidBookingPeriodException(String message) {
13
- super(message);
14
- }
15
-
16
- public InvalidBookingPeriodException(String message, Integer minDays, Integer maxDays, Long requestedDays) {
17
- super(message);
18
- this.minDays = minDays;
19
- this.maxDays = maxDays;
20
- this.requestedDays = requestedDays;
21
- }
22
-
23
- public InvalidBookingPeriodException(String message, Throwable cause) {
24
- super(message, cause);
25
- }
26
-
27
- public Integer getMinDays() {
28
- return minDays;
29
- }
30
-
31
- public Integer getMaxDays() {
32
- return maxDays;
33
- }
34
-
35
- public Long getRequestedDays() {
36
- return requestedDays;
37
- }
38
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/src/main/java/com/locallend/locallend/exception/InvalidOperationException.java DELETED
@@ -1,30 +0,0 @@
1
- package com.locallend.locallend.exception;
2
-
3
- /**
4
- * Exception thrown when an operation cannot be performed due to the current state.
5
- * Examples: Cannot delete active booking, cannot modify completed booking, etc.
6
- */
7
- public class InvalidOperationException extends RuntimeException {
8
- private final String operation;
9
- private final String reason;
10
-
11
- public InvalidOperationException(String operation, String reason) {
12
- super(String.format("Cannot perform operation '%s': %s", operation, reason));
13
- this.operation = operation;
14
- this.reason = reason;
15
- }
16
-
17
- public InvalidOperationException(String message) {
18
- super(message);
19
- this.operation = null;
20
- this.reason = null;
21
- }
22
-
23
- public String getOperation() {
24
- return operation;
25
- }
26
-
27
- public String getReason() {
28
- return reason;
29
- }
30
- }