first week launch

This commit is contained in:
Wowieee4
2026-04-22 20:57:19 +07:00
commit 65b87d3d9d
170 changed files with 7378 additions and 0 deletions
@@ -0,0 +1,13 @@
package com.walkguide;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@@ -0,0 +1,48 @@
package com.walkguide.config;
import org.springframework.boot.CommandLineRunner;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import com.walkguide.entity.User;
import com.walkguide.repository.UserRepository;
import lombok.RequiredArgsConstructor;
@Component
@RequiredArgsConstructor
public class DataSeeder implements CommandLineRunner {
private final UserRepository userRepository;
@Override
public void run(String... args) throws Exception {
// Mesin enkripsi password
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
// Ngecek kalau tabel users kosong, baru diisi
if (userRepository.count() == 0) {
// 1. Bikin akun Guardian (Pendamping)
User guardian = User.builder()
.email("guardian@walkguide.com")
.password(passwordEncoder.encode("guardian123")) // Password barunya
.role("ROLE_GUARDIAN") // Role udah diganti
.build();
// 2. Bikin akun User (Tunanetra)
User user = User.builder()
.email("user@walkguide.com")
.password(passwordEncoder.encode("user123"))
.role("ROLE_USER")
.build();
// Simpan ke database
userRepository.save(guardian);
userRepository.save(user);
System.out.println("✅ Data Seeder berhasil: Akun Guardian dan User telah masuk ke Database!");
}
}
}
@@ -0,0 +1,19 @@
package com.walkguide.config;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class OpenApiConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("Walk Guide API")
.version("1.0")
.description("API Documentation for Walk Guide Application (Final Exam)"));
}
}
@@ -0,0 +1,47 @@
package com.walkguide.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
// MESIN HASHING PASSWORD: Biar password yang disimpan di database gak bisa dibaca langsung, kita enkripsi dulu pake BCrypt
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// Aturan jalan masuk ke API kita:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors(cors -> cors.configurationSource(request -> {
var corsConfiguration = new org.springframework.web.cors.CorsConfiguration();
// Izinkan semua origin localhost dan semua port (biar Chrome gak ngeblok)
corsConfiguration.setAllowedOriginPatterns(java.util.List.of(
"http://localhost:*",
"http://127.0.0.1:*"
));
corsConfiguration.setAllowedMethods(java.util.List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
corsConfiguration.setAllowedHeaders(java.util.List.of("*"));
corsConfiguration.setAllowCredentials(true);
return corsConfiguration;
}))
.csrf(csrf -> csrf.disable())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
}
@@ -0,0 +1,35 @@
package com.walkguide.controller;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.walkguide.dto.AuthRequest;
import com.walkguide.dto.AuthResponse;
import com.walkguide.service.AuthService;
import lombok.RequiredArgsConstructor;
@RestController
@RequestMapping("/api/auth") // Ini URL awalnya
@RequiredArgsConstructor
public class AuthController {
private final AuthService authService;
// Ini URL lengkapnya jadi POST http://localhost:8080/api/auth/login
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody AuthRequest request) {
try {
// Lempar kardus AuthRequest ke Service
AuthResponse response = authService.login(request);
// Kalau sukses, kirim status 200 OK + isinya
return ResponseEntity.ok(response);
} catch (RuntimeException e) {
// Kalau gagal (password salah/email gak ada), kirim status 401 Unauthorized
return ResponseEntity.status(401).body(e.getMessage());
}
}
}
@@ -0,0 +1,43 @@
package com.walkguide.dto;
import java.time.Instant;
public class ApiResponse<T> {
private boolean success;
private T data;
private String message;
private String errorCode;
private String timestamp;
// Constructor buat Sukses
public ApiResponse(boolean success, T data, String message) {
this.success = success;
this.data = data;
this.message = message;
this.timestamp = Instant.now().toString();
}
// Constructor buat Error
public ApiResponse(boolean success, String errorCode, String message) {
this.success = success;
this.errorCode = errorCode;
this.message = message;
this.timestamp = Instant.now().toString();
}
// Getter Setter manual
public boolean isSuccess() { return success; }
public void setSuccess(boolean success) { this.success = success; }
public T getData() { return data; }
public void setData(T data) { this.data = data; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public String getErrorCode() { return errorCode; }
public void setErrorCode(String errorCode) { this.errorCode = errorCode; }
public String getTimestamp() { return timestamp; }
public void setTimestamp(String timestamp) { this.timestamp = timestamp; }
}
@@ -0,0 +1,19 @@
package com.walkguide.dto;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
public class AuthRequest {
@NotBlank(message = "Email tidak boleh kosong")
@Email(message = "Format email tidak valid")
private String email;
@NotBlank(message = "Password tidak boleh kosong")
private String password;
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
@@ -0,0 +1,28 @@
package com.walkguide.dto;
public class AuthResponse {
private String token;
private String role;
public AuthResponse(String token, String role) {
this.token = token;
this.role = role;
}
// Manual Getter Setter (Gantiin fungsi @Data)
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}
@@ -0,0 +1,37 @@
package com.walkguide.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "users")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {
// Ini otomatis bikin ID angka urut (1, 2, 3, dst)
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// Email gak boleh kosong dan gak boleh ada yang kembar
@Column(unique = true, nullable = false)
private String email;
@Column(nullable = false)
private String password;
// Buat nandain dia ini ROLE_USER atau ROLE_ADMIN
@Column(nullable = false)
private String role;
}
@@ -0,0 +1,27 @@
package com.walkguide.exception;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import com.walkguide.dto.ApiResponse;
@ControllerAdvice
public class GlobalExceptionHandler {
// Nangkep error kalau login gagal / user gak ketemu
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<ApiResponse<Object>> handleRuntimeException(RuntimeException ex) {
ApiResponse<Object> response = new ApiResponse<>(false, "AUTH_ERROR", ex.getMessage());
return ResponseEntity.status(401).body(response);
}
// Nangkep error dari Validasi (misal email format salah)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ApiResponse<Object>> handleValidationException(MethodArgumentNotValidException ex) {
String errorMsg = ex.getBindingResult().getAllErrors().get(0).getDefaultMessage();
ApiResponse<Object> response = new ApiResponse<>(false, "VALIDATION_ERROR", errorMsg);
return ResponseEntity.status(400).body(response);
}
}
@@ -0,0 +1,16 @@
package com.walkguide.repository;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.walkguide.entity.User;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// Ini fungsi ajaib Spring Boot. Cuma dari nama fungsinya aja,
// dia udah tau maksudnya "Tolong cariin user di database yang emailnya = X"
Optional<User> findByEmail(String email);
}
@@ -0,0 +1,34 @@
package com.walkguide.security;
import java.security.Key;
import java.util.Date;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
@Component
public class JwtUtil {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
private Key getSigningKey() {
return Keys.hmacShaKeyFor(secret.getBytes());
}
public String generateToken(String email, String role) {
return Jwts.builder()
.setSubject(email)
.claim("role", role)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(getSigningKey())
.compact();
}
}
@@ -0,0 +1,36 @@
package com.walkguide.service;
import com.walkguide.dto.AuthRequest;
import com.walkguide.dto.AuthResponse;
import com.walkguide.entity.User;
import com.walkguide.repository.UserRepository;
import com.walkguide.security.JwtUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class AuthService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final JwtUtil jwtUtil;
public AuthResponse login(AuthRequest request) {
// 1. Cari user di database kampus lu berdasarkan email
User user = userRepository.findByEmail(request.getEmail())
.orElseThrow(() -> new RuntimeException("Email tidak ditemukan!"));
// 2. Cocokin password yang diketik sama password yang di-hash di database
if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) {
throw new RuntimeException("Password salah!");
}
// 3. Kalau email dan password bener, suruh JwtUtil cetak Token
String token = jwtUtil.generateToken(user.getEmail(), user.getRole());
// 4. Bungkus token dan rolenya buat dikirim balik ke Flutter
return new AuthResponse(token, user.getRole());
}
}
@@ -0,0 +1,17 @@
# Server Port
server.port=8080
# === KONEKSI H2 VIRTUAL (GAK PAKE PASSWORD) ===
spring.datasource.url=jdbc:h2:mem:walkguidedb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
# Biar Spring Boot otomatis bikin tabel
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
# JWT Configuration
jwt.secret=WalkGuideSecretKeyYangSangatPanjangMinimal256BitUntukKeamananWalkGuide
jwt.expiration=86400000
@@ -0,0 +1,13 @@
package com.walkguide;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class DemoApplicationTests {
@Test
void contextLoads() {
}
}