sprygen 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +80 -0
  2. package/dist/cli.js +55 -0
  3. package/package.json +53 -0
  4. package/templates/auth/AuthController.java.ejs +40 -0
  5. package/templates/auth/JwtAuthFilter.java.ejs +62 -0
  6. package/templates/auth/JwtService.java.ejs +81 -0
  7. package/templates/auth/SecurityConfig.java.ejs +65 -0
  8. package/templates/auth/UserDetailsServiceImpl.java.ejs +24 -0
  9. package/templates/entity/Entity.java.ejs +40 -0
  10. package/templates/entity/EntityController.java.ejs +92 -0
  11. package/templates/entity/EntityControllerTest.java.ejs +24 -0
  12. package/templates/entity/EntityDto.java.ejs +32 -0
  13. package/templates/entity/EntityRepository.java.ejs +9 -0
  14. package/templates/entity/EntityService.java.ejs +32 -0
  15. package/templates/project/java/config/CorsConfig.java.ejs +24 -0
  16. package/templates/project/java/config/SecurityConfig.java.ejs +76 -0
  17. package/templates/project/java/config/SecurityConfigSession.java.ejs +73 -0
  18. package/templates/project/java/config/SwaggerConfig.java.ejs +31 -0
  19. package/templates/project/java/controller/AdminController.java.ejs +82 -0
  20. package/templates/project/java/controller/AuthController.java.ejs +86 -0
  21. package/templates/project/java/controller/HomeController.java.ejs +63 -0
  22. package/templates/project/java/controller/ProfileController.java.ejs +65 -0
  23. package/templates/project/java/controller/UserController.java.ejs +35 -0
  24. package/templates/project/java/dto/AuthRequest.java.ejs +15 -0
  25. package/templates/project/java/dto/AuthResponse.java.ejs +18 -0
  26. package/templates/project/java/dto/ProfileUpdateRequest.java.ejs +20 -0
  27. package/templates/project/java/dto/RegisterRequest.java.ejs +30 -0
  28. package/templates/project/java/dto/UserDto.java.ejs +17 -0
  29. package/templates/project/java/entity/Role.java.ejs +6 -0
  30. package/templates/project/java/entity/User.java.ejs +97 -0
  31. package/templates/project/java/repository/UserRepository.java.ejs +11 -0
  32. package/templates/project/java/security/JwtAuthFilter.java.ejs +62 -0
  33. package/templates/project/java/security/UserDetailsServiceImpl.java.ejs +21 -0
  34. package/templates/project/java/service/JwtService.java.ejs +81 -0
  35. package/templates/project/java/service/UserService.java.ejs +32 -0
  36. package/templates/project/resources/application.yml.ejs +50 -0
  37. package/templates/project/resources/logback-spring.xml.ejs +41 -0
  38. package/templates/project/static/admin.html.ejs +163 -0
  39. package/templates/project/static/assets/app.js.ejs +340 -0
  40. package/templates/project/static/assets/style.css +533 -0
  41. package/templates/project/static/css/style.css +595 -0
  42. package/templates/project/static/dashboard.html.ejs +119 -0
  43. package/templates/project/static/index.html.ejs +96 -0
  44. package/templates/project/static/js/api.js +30 -0
  45. package/templates/project/static/js/auth.js +44 -0
  46. package/templates/project/static/js/nav.js.ejs +82 -0
  47. package/templates/project/static/js/ui.js +57 -0
  48. package/templates/project/static/login.html.ejs +71 -0
  49. package/templates/project/static/profile.html.ejs +163 -0
  50. package/templates/project/static/register.html.ejs +82 -0
  51. package/templates/project/thymeleaf/admin/users.html.ejs +111 -0
  52. package/templates/project/thymeleaf/dashboard.html.ejs +109 -0
  53. package/templates/project/thymeleaf/layout.html.ejs +75 -0
  54. package/templates/project/thymeleaf/login.html.ejs +56 -0
  55. package/templates/project/thymeleaf/profile.html.ejs +133 -0
  56. package/templates/project/thymeleaf/register.html.ejs +56 -0
@@ -0,0 +1,24 @@
1
+ package <%= packageName %>.controller;
2
+
3
+ import org.junit.jupiter.api.Test;
4
+ import org.springframework.beans.factory.annotation.Autowired;
5
+ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
6
+ import org.springframework.boot.test.context.SpringBootTest;
7
+ import org.springframework.test.web.servlet.MockMvc;
8
+
9
+ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
10
+ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
11
+
12
+ @SpringBootTest
13
+ @AutoConfigureMockMvc
14
+ class <%= entityName %>ControllerTest {
15
+
16
+ @Autowired
17
+ private MockMvc mockMvc;
18
+
19
+ @Test
20
+ void testGetAll() throws Exception {
21
+ mockMvc.perform(get("/api/v1/<%= entityNameLower %>s"))
22
+ .andExpect(status().isOk());
23
+ }
24
+ }
@@ -0,0 +1,32 @@
1
+ package <%= packageName %>.dto;
2
+
3
+ import lombok.AllArgsConstructor;
4
+ import lombok.Builder;
5
+ import lombok.Data;
6
+ import lombok.NoArgsConstructor;
7
+
8
+ <%_
9
+ const hasLocalDate = fields.some(f => f.type === 'LocalDate');
10
+ const hasLocalDateTime = fields.some(f => f.type === 'LocalDateTime');
11
+
12
+ if (hasLocalDate) { _%>
13
+ import java.time.LocalDate;
14
+ <%_ }
15
+ if (hasLocalDateTime) { _%>
16
+ import java.time.LocalDateTime;
17
+ <%_ } _%>
18
+
19
+ @Data
20
+ @Builder
21
+ @NoArgsConstructor
22
+ @AllArgsConstructor
23
+ public class <%= entityName %>Dto {
24
+
25
+ private Long id;
26
+
27
+ <%_ for (let i = 0; i < fields.length; i++) {
28
+ let field = fields[i]; _%>
29
+ private <%= field.type %> <%= field.name %>;
30
+
31
+ <%_ } _%>
32
+ }
@@ -0,0 +1,9 @@
1
+ package <%= packageName %>.repository;
2
+
3
+ import <%= packageName %>.entity.<%= entityName %>;
4
+ import org.springframework.data.jpa.repository.JpaRepository;
5
+ import org.springframework.stereotype.Repository;
6
+
7
+ @Repository
8
+ public interface <%= entityName %>Repository extends JpaRepository<<%= entityName %>, Long> {
9
+ }
@@ -0,0 +1,32 @@
1
+ package <%= packageName %>.service;
2
+
3
+ import <%= packageName %>.entity.<%= entityName %>;
4
+ import <%= packageName %>.repository.<%= entityName %>Repository;
5
+ import lombok.RequiredArgsConstructor;
6
+ import org.springframework.stereotype.Service;
7
+
8
+ import java.util.List;
9
+ import java.util.Optional;
10
+
11
+ @Service
12
+ @RequiredArgsConstructor
13
+ public class <%= entityName %>Service {
14
+
15
+ private final <%= entityName %>Repository repository;
16
+
17
+ public List<<%= entityName %>> findAll() {
18
+ return repository.findAll();
19
+ }
20
+
21
+ public Optional<<%= entityName %>> findById(Long id) {
22
+ return repository.findById(id);
23
+ }
24
+
25
+ public <%= entityName %> save(<%= entityName %> entity) {
26
+ return repository.save(entity);
27
+ }
28
+
29
+ public void deleteById(Long id) {
30
+ repository.deleteById(id);
31
+ }
32
+ }
@@ -0,0 +1,24 @@
1
+ package <%= packageName %>.config;
2
+
3
+ import org.springframework.context.annotation.Bean;
4
+ import org.springframework.context.annotation.Configuration;
5
+ import org.springframework.web.servlet.config.annotation.CorsRegistry;
6
+ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
7
+
8
+ @Configuration
9
+ public class CorsConfig {
10
+
11
+ @Bean
12
+ public WebMvcConfigurer corsConfigurer() {
13
+ return new WebMvcConfigurer() {
14
+ @Override
15
+ public void addCorsMappings(CorsRegistry registry) {
16
+ registry.addMapping("/**")
17
+ .allowedOrigins("*") // In production, restrict this to specific domains
18
+ .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH")
19
+ .allowedHeaders("*")
20
+ .maxAge(3600);
21
+ }
22
+ };
23
+ }
24
+ }
@@ -0,0 +1,76 @@
1
+ package <%= packageName %>.config;
2
+
3
+ import <%= packageName %>.security.JwtAuthFilter;
4
+ import lombok.RequiredArgsConstructor;
5
+ import org.springframework.context.annotation.Bean;
6
+ import org.springframework.context.annotation.Configuration;
7
+ import org.springframework.security.authentication.AuthenticationManager;
8
+ import org.springframework.security.authentication.AuthenticationProvider;
9
+ import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
10
+ import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
11
+ import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
12
+ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
13
+ import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
14
+ import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
15
+ import org.springframework.security.config.http.SessionCreationPolicy;
16
+ import org.springframework.security.core.userdetails.UserDetailsService;
17
+ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
18
+ import org.springframework.security.crypto.password.PasswordEncoder;
19
+ import org.springframework.security.web.SecurityFilterChain;
20
+ import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
21
+
22
+ @Configuration
23
+ @EnableWebSecurity
24
+ @EnableMethodSecurity
25
+ @RequiredArgsConstructor
26
+ public class SecurityConfig {
27
+
28
+ private final JwtAuthFilter jwtAuthFilter;
29
+ private final UserDetailsService userDetailsService;
30
+
31
+ @Bean
32
+ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
33
+ http
34
+ .csrf(AbstractHttpConfigurer::disable)
35
+ .authorizeHttpRequests(authorize -> authorize
36
+ // Public auth endpoints
37
+ .requestMatchers("/api/v1/auth/**").permitAll()
38
+ // Public home / static assets (individual pages + assets)
39
+ .requestMatchers("/", "/index.html", "/login.html", "/register.html").permitAll()
40
+ .requestMatchers("/dashboard.html", "/profile.html", "/admin.html").permitAll()
41
+ .requestMatchers("/assets/**", "/css/**", "/js/**", "/favicon.ico").permitAll()
42
+ <%_ if (hasSwagger) { _%>
43
+ .requestMatchers("/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll()
44
+ <%_ } _%>
45
+ .requestMatchers("/actuator/**").permitAll()
46
+ // Admin endpoints — ROLE_ADMIN enforced via @PreAuthorize too
47
+ .requestMatchers("/api/v1/admin/**").hasAuthority("ROLE_ADMIN")
48
+ .anyRequest().authenticated()
49
+ )
50
+ .sessionManagement(session -> session
51
+ .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
52
+ )
53
+ .authenticationProvider(authenticationProvider())
54
+ .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
55
+
56
+ return http.build();
57
+ }
58
+
59
+ @Bean
60
+ public AuthenticationProvider authenticationProvider() {
61
+ // Spring Security 6.x: UserDetailsService must be passed to the constructor
62
+ DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(userDetailsService);
63
+ authProvider.setPasswordEncoder(passwordEncoder());
64
+ return authProvider;
65
+ }
66
+
67
+ @Bean
68
+ public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
69
+ return config.getAuthenticationManager();
70
+ }
71
+
72
+ @Bean
73
+ public PasswordEncoder passwordEncoder() {
74
+ return new BCryptPasswordEncoder();
75
+ }
76
+ }
@@ -0,0 +1,73 @@
1
+ package <%= packageName %>.config;
2
+
3
+ import lombok.RequiredArgsConstructor;
4
+ import org.springframework.context.annotation.Bean;
5
+ import org.springframework.context.annotation.Configuration;
6
+ import org.springframework.security.authentication.AuthenticationManager;
7
+ import org.springframework.security.authentication.AuthenticationProvider;
8
+ import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
9
+ import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
10
+ import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
11
+ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
12
+ import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
13
+ import org.springframework.security.core.userdetails.UserDetailsService;
14
+ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
15
+ import org.springframework.security.crypto.password.PasswordEncoder;
16
+ import org.springframework.security.web.SecurityFilterChain;
17
+
18
+ @Configuration
19
+ @EnableWebSecurity
20
+ @EnableMethodSecurity
21
+ @RequiredArgsConstructor
22
+ public class SecurityConfig {
23
+
24
+ private final UserDetailsService userDetailsService;
25
+
26
+ @Bean
27
+ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
28
+ http
29
+ .authorizeHttpRequests(authorize -> authorize
30
+ .requestMatchers("/", "/login", "/register", "/css/**", "/js/**", "/assets/**", "/favicon.ico").permitAll()
31
+ <%_ if (hasSwagger) { _%>
32
+ .requestMatchers("/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll()
33
+ <%_ } _%>
34
+ .requestMatchers("/actuator/**").permitAll()
35
+ .requestMatchers("/admin/**").hasAuthority("ROLE_ADMIN")
36
+ .anyRequest().authenticated()
37
+ )
38
+ .formLogin(form -> form
39
+ .loginPage("/login")
40
+ .loginProcessingUrl("/login")
41
+ .defaultSuccessUrl("/dashboard", true)
42
+ .failureUrl("/login?error=true")
43
+ .permitAll()
44
+ )
45
+ .logout(logout -> logout
46
+ .logoutUrl("/logout")
47
+ .logoutSuccessUrl("/")
48
+ .invalidateHttpSession(true)
49
+ .deleteCookies("JSESSIONID")
50
+ .permitAll()
51
+ )
52
+ .authenticationProvider(authenticationProvider());
53
+
54
+ return http.build();
55
+ }
56
+
57
+ @Bean
58
+ public AuthenticationProvider authenticationProvider() {
59
+ DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(userDetailsService);
60
+ authProvider.setPasswordEncoder(passwordEncoder());
61
+ return authProvider;
62
+ }
63
+
64
+ @Bean
65
+ public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
66
+ return config.getAuthenticationManager();
67
+ }
68
+
69
+ @Bean
70
+ public PasswordEncoder passwordEncoder() {
71
+ return new BCryptPasswordEncoder();
72
+ }
73
+ }
@@ -0,0 +1,31 @@
1
+ package <%= packageName %>.config;
2
+
3
+ import io.swagger.v3.oas.models.Components;
4
+ import io.swagger.v3.oas.models.OpenAPI;
5
+ import io.swagger.v3.oas.models.info.Info;
6
+ import io.swagger.v3.oas.models.security.SecurityRequirement;
7
+ import io.swagger.v3.oas.models.security.SecurityScheme;
8
+ import org.springframework.context.annotation.Bean;
9
+ import org.springframework.context.annotation.Configuration;
10
+
11
+ @Configuration
12
+ public class SwaggerConfig {
13
+
14
+ @Bean
15
+ public OpenAPI customOpenAPI() {
16
+ final String securitySchemeName = "bearerAuth";
17
+ return new OpenAPI()
18
+ .info(new Info().title("<%= projectName %> API").version("1.0.0").description("<%= description %>"))
19
+ .addSecurityItem(new SecurityRequirement().addList(securitySchemeName))
20
+ .components(
21
+ new Components()
22
+ .addSecuritySchemes(securitySchemeName,
23
+ new SecurityScheme()
24
+ .name(securitySchemeName)
25
+ .type(SecurityScheme.Type.HTTP)
26
+ .scheme("bearer")
27
+ .bearerFormat("JWT")
28
+ )
29
+ );
30
+ }
31
+ }
@@ -0,0 +1,82 @@
1
+ package <%= packageName %>.controller;
2
+
3
+ import <%= packageName %>.entity.Role;
4
+ import <%= packageName %>.entity.User;
5
+ import <%= packageName %>.repository.UserRepository;
6
+ import lombok.RequiredArgsConstructor;
7
+ import org.springframework.http.ResponseEntity;
8
+ import org.springframework.security.access.prepost.PreAuthorize;
9
+ import org.springframework.web.bind.annotation.*;
10
+
11
+ import java.util.List;
12
+ import java.util.Map;
13
+ import java.util.Set;
14
+
15
+ @RestController
16
+ @RequestMapping("/api/v1/admin")
17
+ @RequiredArgsConstructor
18
+ @PreAuthorize("hasAuthority('ROLE_ADMIN')")
19
+ public class AdminController {
20
+
21
+ private final UserRepository userRepository;
22
+
23
+ /** GET /api/v1/admin/users — list all users */
24
+ @GetMapping("/users")
25
+ public ResponseEntity<List<User>> listUsers() {
26
+ return ResponseEntity.ok(userRepository.findAll());
27
+ }
28
+
29
+ /** GET /api/v1/admin/users/{id} — get single user */
30
+ @GetMapping("/users/{id}")
31
+ public ResponseEntity<User> getUser(@PathVariable Long id) {
32
+ return userRepository.findById(id)
33
+ .map(ResponseEntity::ok)
34
+ .orElse(ResponseEntity.notFound().build());
35
+ }
36
+
37
+ /** PUT /api/v1/admin/users/{id}/role — assign role */
38
+ @PutMapping("/users/{id}/role")
39
+ public ResponseEntity<Map<String, String>> assignRole(
40
+ @PathVariable Long id,
41
+ @RequestBody Map<String, String> body) {
42
+
43
+ String roleName = body.get("role");
44
+ Role role;
45
+ try {
46
+ role = Role.valueOf(roleName);
47
+ } catch (IllegalArgumentException e) {
48
+ return ResponseEntity.badRequest()
49
+ .body(Map.of("error", "Invalid role: " + roleName + ". Use ROLE_USER or ROLE_ADMIN"));
50
+ }
51
+
52
+ return userRepository.findById(id).map(user -> {
53
+ user.setRoles(Set.of(role));
54
+ userRepository.save(user);
55
+ return ResponseEntity.ok(Map.of("message", "Role updated to " + roleName));
56
+ }).orElse(ResponseEntity.notFound().build());
57
+ }
58
+
59
+ /** DELETE /api/v1/admin/users/{id} — delete user */
60
+ @DeleteMapping("/users/{id}")
61
+ public ResponseEntity<Map<String, String>> deleteUser(@PathVariable Long id) {
62
+ if (!userRepository.existsById(id)) {
63
+ return ResponseEntity.notFound().build();
64
+ }
65
+ userRepository.deleteById(id);
66
+ return ResponseEntity.ok(Map.of("message", "User deleted"));
67
+ }
68
+
69
+ /** GET /api/v1/admin/stats — quick dashboard stats */
70
+ @GetMapping("/stats")
71
+ public ResponseEntity<Map<String, Long>> stats() {
72
+ long total = userRepository.count();
73
+ long admins = userRepository.findAll().stream()
74
+ .filter(User::isAdmin)
75
+ .count();
76
+ return ResponseEntity.ok(Map.of(
77
+ "totalUsers", total,
78
+ "admins", admins,
79
+ "regularUsers", total - admins
80
+ ));
81
+ }
82
+ }
@@ -0,0 +1,86 @@
1
+ package <%= packageName %>.controller;
2
+
3
+ import <%= packageName %>.dto.AuthRequest;
4
+ import <%= packageName %>.dto.AuthResponse;
5
+ import <%= packageName %>.dto.RegisterRequest;
6
+ import <%= packageName %>.entity.Role;
7
+ import <%= packageName %>.entity.User;
8
+ import <%= packageName %>.repository.UserRepository;
9
+ import <%= packageName %>.service.JwtService;
10
+ import jakarta.validation.Valid;
11
+ import lombok.RequiredArgsConstructor;
12
+ import org.springframework.http.ResponseEntity;
13
+ import org.springframework.security.authentication.AuthenticationManager;
14
+ import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
15
+ import org.springframework.security.crypto.password.PasswordEncoder;
16
+ import org.springframework.web.bind.annotation.*;
17
+
18
+ import java.util.HashSet;
19
+ import java.util.Set;
20
+
21
+ @RestController
22
+ @RequestMapping("/api/v1/auth")
23
+ @RequiredArgsConstructor
24
+ public class AuthController {
25
+
26
+ private final UserRepository userRepository;
27
+ private final PasswordEncoder passwordEncoder;
28
+ private final JwtService jwtService;
29
+ private final AuthenticationManager authenticationManager;
30
+
31
+ @PostMapping("/register")
32
+ public ResponseEntity<AuthResponse> register(@Valid @RequestBody RegisterRequest request) {
33
+ if (userRepository.existsByEmail(request.getEmail())) {
34
+ return ResponseEntity.badRequest().build();
35
+ }
36
+
37
+ // First registered user becomes ADMIN, everyone else is USER
38
+ boolean isFirstUser = userRepository.count() == 0;
39
+ Set<Role> roles = isFirstUser
40
+ ? new HashSet<>(Set.of(Role.ROLE_ADMIN, Role.ROLE_USER))
41
+ : new HashSet<>(Set.of(Role.ROLE_USER));
42
+
43
+ var user = User.builder()
44
+ .firstName(request.getFirstName())
45
+ .lastName(request.getLastName())
46
+ .email(request.getEmail())
47
+ .password(passwordEncoder.encode(request.getPassword()))
48
+ .roles(roles)
49
+ .build();
50
+
51
+ userRepository.save(user);
52
+ var jwtToken = jwtService.generateToken(user);
53
+
54
+ return ResponseEntity.ok(AuthResponse.builder()
55
+ .token(jwtToken)
56
+ .email(user.getEmail())
57
+ .firstName(user.getFirstName())
58
+ .lastName(user.getLastName())
59
+ .role(isFirstUser ? "ROLE_ADMIN" : "ROLE_USER")
60
+ .build());
61
+ }
62
+
63
+ @PostMapping("/login")
64
+ public ResponseEntity<AuthResponse> login(@Valid @RequestBody AuthRequest request) {
65
+ authenticationManager.authenticate(
66
+ new UsernamePasswordAuthenticationToken(
67
+ request.getEmail(),
68
+ request.getPassword()
69
+ )
70
+ );
71
+
72
+ var user = userRepository.findByEmail(request.getEmail())
73
+ .orElseThrow();
74
+
75
+ var jwtToken = jwtService.generateToken(user);
76
+ String primaryRole = user.isAdmin() ? "ROLE_ADMIN" : "ROLE_USER";
77
+
78
+ return ResponseEntity.ok(AuthResponse.builder()
79
+ .token(jwtToken)
80
+ .email(user.getEmail())
81
+ .firstName(user.getFirstName())
82
+ .lastName(user.getLastName())
83
+ .role(primaryRole)
84
+ .build());
85
+ }
86
+ }
@@ -0,0 +1,63 @@
1
+ package <%= packageName %>.controller;
2
+
3
+ import lombok.RequiredArgsConstructor;
4
+ <%_ if (isFullstack && isJwtAuth) { _%>
5
+ import org.springframework.stereotype.Controller;
6
+ import org.springframework.web.bind.annotation.GetMapping;
7
+
8
+ @Controller
9
+ @RequiredArgsConstructor
10
+ public class HomeController {
11
+
12
+ /** Serve the landing page */
13
+ @GetMapping("/")
14
+ public String home() {
15
+ return "forward:/index.html";
16
+ }
17
+
18
+ /** Each page is standalone — just permit static file serving */
19
+ @GetMapping({"/login", "/register"})
20
+ public String authPages() {
21
+ return "forward:/index.html";
22
+ }
23
+ }
24
+ <%_ } else if (isFullstack && isSessionAuth) { _%>
25
+ import org.springframework.stereotype.Controller;
26
+ import org.springframework.web.bind.annotation.GetMapping;
27
+
28
+ @Controller
29
+ @RequiredArgsConstructor
30
+ public class HomeController {
31
+
32
+ @GetMapping("/")
33
+ public String home() {
34
+ return "redirect:/dashboard";
35
+ }
36
+ }
37
+ <%_ } else { _%>
38
+ import org.springframework.http.ResponseEntity;
39
+ import org.springframework.web.bind.annotation.GetMapping;
40
+ import org.springframework.web.bind.annotation.RestController;
41
+
42
+ import java.time.LocalDateTime;
43
+ import java.util.LinkedHashMap;
44
+ import java.util.Map;
45
+
46
+ @RestController
47
+ @RequiredArgsConstructor
48
+ public class HomeController {
49
+
50
+ @GetMapping("/")
51
+ public ResponseEntity<Map<String, Object>> home() {
52
+ Map<String, Object> info = new LinkedHashMap<>();
53
+ info.put("status", "UP");
54
+ info.put("app", "<%= projectName %>");
55
+ info.put("version", "1.0.0");
56
+ info.put("auth", "JWT");
57
+ info.put("docs", "/swagger-ui/index.html");
58
+ info.put("health", "/actuator/health");
59
+ info.put("time", LocalDateTime.now().toString());
60
+ return ResponseEntity.ok(info);
61
+ }
62
+ }
63
+ <%_ } _%>
@@ -0,0 +1,65 @@
1
+ package <%= packageName %>.controller;
2
+
3
+ import <%= packageName %>.dto.ProfileUpdateRequest;
4
+ import <%= packageName %>.entity.User;
5
+ import <%= packageName %>.repository.UserRepository;
6
+ import lombok.RequiredArgsConstructor;
7
+ import org.springframework.http.ResponseEntity;
8
+ import org.springframework.security.access.prepost.PreAuthorize;
9
+ import org.springframework.security.core.annotation.AuthenticationPrincipal;
10
+ import org.springframework.security.crypto.password.PasswordEncoder;
11
+ import org.springframework.web.bind.annotation.*;
12
+
13
+ import java.util.Map;
14
+
15
+ @RestController
16
+ @RequestMapping("/api/v1/profile")
17
+ @RequiredArgsConstructor
18
+ @PreAuthorize("isAuthenticated()")
19
+ public class ProfileController {
20
+
21
+ private final UserRepository userRepository;
22
+ private final PasswordEncoder passwordEncoder;
23
+
24
+ /** GET /api/v1/profile — get own profile */
25
+ @GetMapping
26
+ public ResponseEntity<User> getProfile(@AuthenticationPrincipal User user) {
27
+ return ResponseEntity.ok(user);
28
+ }
29
+
30
+ /** PUT /api/v1/profile — update name, bio, avatarUrl */
31
+ @PutMapping
32
+ public ResponseEntity<User> updateProfile(
33
+ @AuthenticationPrincipal User user,
34
+ @RequestBody ProfileUpdateRequest request) {
35
+
36
+ if (request.getFirstName() != null) user.setFirstName(request.getFirstName());
37
+ if (request.getLastName() != null) user.setLastName(request.getLastName());
38
+ if (request.getBio() != null) user.setBio(request.getBio());
39
+ if (request.getAvatarUrl() != null) user.setAvatarUrl(request.getAvatarUrl());
40
+
41
+ userRepository.save(user);
42
+ return ResponseEntity.ok(user);
43
+ }
44
+
45
+ /** PUT /api/v1/profile/password — change own password */
46
+ @PutMapping("/password")
47
+ public ResponseEntity<Map<String, String>> changePassword(
48
+ @AuthenticationPrincipal User user,
49
+ @RequestBody ProfileUpdateRequest request) {
50
+
51
+ if (request.getCurrentPassword() == null || request.getNewPassword() == null) {
52
+ return ResponseEntity.badRequest()
53
+ .body(Map.of("error", "currentPassword and newPassword are required"));
54
+ }
55
+
56
+ if (!passwordEncoder.matches(request.getCurrentPassword(), user.getPassword())) {
57
+ return ResponseEntity.status(403)
58
+ .body(Map.of("error", "Current password is incorrect"));
59
+ }
60
+
61
+ user.setPassword(passwordEncoder.encode(request.getNewPassword()));
62
+ userRepository.save(user);
63
+ return ResponseEntity.ok(Map.of("message", "Password updated successfully"));
64
+ }
65
+ }
@@ -0,0 +1,35 @@
1
+ package <%= packageName %>.controller;
2
+
3
+ import <%= packageName %>.entity.User;
4
+ import <%= packageName %>.service.UserService;
5
+ import lombok.RequiredArgsConstructor;
6
+ import org.springframework.http.ResponseEntity;
7
+ import org.springframework.web.bind.annotation.*;
8
+
9
+ import java.util.List;
10
+
11
+ @RestController
12
+ @RequestMapping("/api/v1/users")
13
+ @RequiredArgsConstructor
14
+ public class UserController {
15
+
16
+ private final UserService userService;
17
+
18
+ @GetMapping
19
+ public ResponseEntity<List<User>> getAllUsers() {
20
+ return ResponseEntity.ok(userService.getAllUsers());
21
+ }
22
+
23
+ @GetMapping("/{id}")
24
+ public ResponseEntity<User> getUserById(@PathVariable Long id) {
25
+ return userService.getUserById(id)
26
+ .map(ResponseEntity::ok)
27
+ .orElse(ResponseEntity.notFound().build());
28
+ }
29
+
30
+ @DeleteMapping("/{id}")
31
+ public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
32
+ userService.deleteUser(id);
33
+ return ResponseEntity.noContent().build();
34
+ }
35
+ }
@@ -0,0 +1,15 @@
1
+ package <%= packageName %>.dto;
2
+
3
+ import lombok.AllArgsConstructor;
4
+ import lombok.Builder;
5
+ import lombok.Data;
6
+ import lombok.NoArgsConstructor;
7
+
8
+ @Data
9
+ @Builder
10
+ @AllArgsConstructor
11
+ @NoArgsConstructor
12
+ public class AuthRequest {
13
+ private String email;
14
+ private String password;
15
+ }
@@ -0,0 +1,18 @@
1
+ package <%= packageName %>.dto;
2
+
3
+ import lombok.AllArgsConstructor;
4
+ import lombok.Builder;
5
+ import lombok.Data;
6
+ import lombok.NoArgsConstructor;
7
+
8
+ @Data
9
+ @Builder
10
+ @AllArgsConstructor
11
+ @NoArgsConstructor
12
+ public class AuthResponse {
13
+ private String token;
14
+ private String email;
15
+ private String firstName;
16
+ private String lastName;
17
+ private String role;
18
+ }