first commit. application working
This commit is contained in:
@ -0,0 +1,12 @@
|
||||
|
||||
package com.weighttracker;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class WeightTrackerApplication {
|
||||
public static void main(String[] args){
|
||||
SpringApplication.run(WeightTrackerApplication.class, args);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
|
||||
package com.weighttracker.config;
|
||||
|
||||
import com.weighttracker.repository.UserRepository;
|
||||
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.core.userdetails.*;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
@Configuration
|
||||
public class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
public UserDetailsService uds(UserRepository repo){
|
||||
return username -> repo.findByUsername(username)
|
||||
.map(u -> User.withUsername(u.getUsername())
|
||||
.password(u.getPassword())
|
||||
.roles(u.getRole())
|
||||
.build())
|
||||
.orElseThrow(() -> new UsernameNotFoundException("Not found"));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filter(HttpSecurity http) throws Exception {
|
||||
http.authorizeHttpRequests(a -> a
|
||||
.requestMatchers("/css/**","/js/**","/images/**","/login","/login?error","/login?logout").permitAll()
|
||||
.requestMatchers("/usuarios/**").hasRole("ADMIN")
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.formLogin(login -> login
|
||||
.loginPage("/login")
|
||||
.loginProcessingUrl("/do-login")
|
||||
.defaultSuccessUrl("/", true)
|
||||
.failureUrl("/login?error")
|
||||
.permitAll()
|
||||
)
|
||||
.logout(l -> l.logoutSuccessUrl("/login?logout").permitAll());
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder encoder(){ return new BCryptPasswordEncoder();}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
|
||||
package com.weighttracker.controller;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
@Controller
|
||||
public class AuthController {
|
||||
@GetMapping("/login")
|
||||
public String login(){ return "login"; }
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
package com.weighttracker.controller;
|
||||
|
||||
import com.weighttracker.model.WeightRecord;
|
||||
import com.weighttracker.repository.UserRepository;
|
||||
import com.weighttracker.service.WeightService;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
|
||||
@Controller
|
||||
public class DashboardController {
|
||||
|
||||
private final WeightService weightService;
|
||||
private final UserRepository userRepo;
|
||||
|
||||
public DashboardController(WeightService weightService, UserRepository userRepo) {
|
||||
this.weightService = weightService;
|
||||
this.userRepo = userRepo;
|
||||
}
|
||||
|
||||
@GetMapping("/")
|
||||
public String home(@AuthenticationPrincipal UserDetails ud,
|
||||
@RequestParam(required = false) String start,
|
||||
@RequestParam(required = false) String end,
|
||||
Model model) {
|
||||
|
||||
var user = userRepo.findByUsername(ud.getUsername()).orElseThrow();
|
||||
model.addAttribute("ultimos", weightService.last10(user));
|
||||
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
LocalDateTime startDate = (start != null) ? LocalDate.parse(start, formatter).atStartOfDay()
|
||||
: LocalDateTime.now().minusDays(30);
|
||||
LocalDateTime endDate = (end != null) ? LocalDate.parse(end, formatter).atTime(LocalTime.MAX)
|
||||
: LocalDateTime.now();
|
||||
|
||||
List<WeightRecord> registros = weightService.recordsBetween(user, startDate, endDate);
|
||||
model.addAttribute("labels", registros.stream().map(r -> r.getDate().toLocalDate().toString()).toList());
|
||||
model.addAttribute("pesos", registros.stream().map(WeightRecord::getWeight).toList());
|
||||
model.addAttribute("grasa", registros.stream().map(WeightRecord::getBodyFat).toList());
|
||||
model.addAttribute("musculo", registros.stream().map(WeightRecord::getMuscleMass).toList());
|
||||
model.addAttribute("agua", registros.stream().map(WeightRecord::getWaterPercent).toList());
|
||||
model.addAttribute("bmi", registros.stream().map(WeightRecord::getBmi).toList());
|
||||
model.addAttribute("edadMetabolica", registros.stream().map(WeightRecord::getMetabolicAge).toList());
|
||||
model.addAttribute("grasaVisceral", registros.stream().map(WeightRecord::getVisceralFat).toList());
|
||||
|
||||
return "dashboard";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
package com.weighttracker.controller;
|
||||
|
||||
import com.weighttracker.model.User;
|
||||
import com.weighttracker.service.UserService;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/usuarios")
|
||||
public class UserController {
|
||||
|
||||
private final UserService service;
|
||||
|
||||
public UserController(UserService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String list(Model m) {
|
||||
m.addAttribute("usuarios", service.findAll());
|
||||
return "usuarios";
|
||||
}
|
||||
|
||||
@GetMapping("/nuevo")
|
||||
public String nuevo(Model m) {
|
||||
m.addAttribute("usuario", new User());
|
||||
return "usuario-form";
|
||||
}
|
||||
|
||||
@PostMapping("/guardar")
|
||||
public String guardar(@ModelAttribute("usuario") User u) {
|
||||
service.save(u);
|
||||
return "redirect:/usuarios";
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/editar")
|
||||
public String editar(@PathVariable Long id, Model model) {
|
||||
User user = service.findById(id);
|
||||
model.addAttribute("usuario", user);
|
||||
return "usuario-form";
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/eliminar")
|
||||
public String eliminar(@PathVariable Long id) {
|
||||
service.deleteById(id);
|
||||
return "redirect:/usuarios";
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
package com.weighttracker.controller;
|
||||
|
||||
import com.weighttracker.model.WeightRecord;
|
||||
import com.weighttracker.model.User;
|
||||
import com.weighttracker.repository.UserRepository;
|
||||
import com.weighttracker.service.WeightService;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/registros")
|
||||
public class WeightController {
|
||||
|
||||
private final WeightService weightService;
|
||||
private final UserRepository userRepo;
|
||||
|
||||
public WeightController(WeightService weightService, UserRepository userRepo) {
|
||||
this.weightService = weightService;
|
||||
this.userRepo = userRepo;
|
||||
}
|
||||
|
||||
/* ---------- listado ---------- */
|
||||
@GetMapping
|
||||
public String list(@AuthenticationPrincipal UserDetails ud, Model model) {
|
||||
User u = userRepo.findByUsername(ud.getUsername()).orElseThrow();
|
||||
model.addAttribute("registros", weightService.findByUser(u));
|
||||
return "registros";
|
||||
}
|
||||
|
||||
/* ---------- nuevo ---------- */
|
||||
@GetMapping("/nuevo")
|
||||
public String nuevo(Model model) {
|
||||
WeightRecord wr = new WeightRecord();
|
||||
wr.setDate(LocalDateTime.now());
|
||||
model.addAttribute("registro", wr);
|
||||
return "registro-form";
|
||||
}
|
||||
|
||||
@PostMapping("/guardar")
|
||||
public String guardar(@AuthenticationPrincipal UserDetails ud,
|
||||
@ModelAttribute("registro") @Valid WeightRecord wr) {
|
||||
wr.setUser(userRepo.findByUsername(ud.getUsername()).orElseThrow());
|
||||
weightService.save(wr);
|
||||
return "redirect:/registros";
|
||||
}
|
||||
|
||||
/* ---------- editar ---------- */
|
||||
@GetMapping("/{id}/editar")
|
||||
public String editar(@PathVariable Long id, Model model) {
|
||||
model.addAttribute("registro", weightService.findById(id));
|
||||
return "registro-form";
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/eliminar")
|
||||
public String eliminar(@PathVariable Long id, @AuthenticationPrincipal UserDetails ud) {
|
||||
User user = userRepo.findByUsername(ud.getUsername()).orElseThrow();
|
||||
WeightRecord record = weightService.findById(id);
|
||||
|
||||
// Solo permite borrar si es el dueño o si es ADMIN
|
||||
if (record.getUser().getId().equals(user.getId()) || user.getRole().equals("ADMIN")) {
|
||||
weightService.deleteById(id);
|
||||
}
|
||||
|
||||
return "redirect:/registros";
|
||||
}
|
||||
}
|
||||
24
backend/src/main/java/com/weighttracker/model/User.java
Normal file
24
backend/src/main/java/com/weighttracker/model/User.java
Normal file
@ -0,0 +1,24 @@
|
||||
|
||||
package com.weighttracker.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
@Entity
|
||||
@Table(name = "users")
|
||||
@Getter @Setter
|
||||
@NoArgsConstructor @AllArgsConstructor
|
||||
public class User {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(unique=true, nullable=false)
|
||||
private String username;
|
||||
|
||||
@Column(nullable=false)
|
||||
private String password;
|
||||
|
||||
@Column(nullable=false)
|
||||
private String role; // ADMIN / USER
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
|
||||
package com.weighttracker.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "weight_record")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class WeightRecord {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "user_id")
|
||||
private User user;
|
||||
|
||||
private LocalDateTime date;
|
||||
private Double weight;
|
||||
private Double bodyFat;
|
||||
private Double muscleMass;
|
||||
private Double waterPercent;
|
||||
private Double bmi;
|
||||
private Integer metabolicAge;
|
||||
private Integer visceralFat;
|
||||
@Column(columnDefinition = "text")
|
||||
private String notes;
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
|
||||
package com.weighttracker.repository;
|
||||
|
||||
import com.weighttracker.model.User;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface UserRepository extends JpaRepository<User, Long> {
|
||||
Optional<User> findByUsername(String username);
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
package com.weighttracker.repository;
|
||||
|
||||
import com.weighttracker.model.User;
|
||||
import com.weighttracker.model.WeightRecord;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
public interface WeightRecordRepository extends JpaRepository<WeightRecord, Long> {
|
||||
List<WeightRecord> findTop10ByUserOrderByDateDesc(User user);
|
||||
List<WeightRecord> findByUserOrderByDateDesc(User user);
|
||||
List<WeightRecord> findByUserAndDateAfterOrderByDateAsc(User user, LocalDateTime date);
|
||||
List<WeightRecord> findByUserAndDateBetweenOrderByDateAsc(User user, LocalDateTime start, LocalDateTime end);
|
||||
void deleteAllByUser(User user);
|
||||
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
|
||||
package com.weighttracker.service;
|
||||
|
||||
import com.weighttracker.model.User;
|
||||
import com.weighttracker.repository.UserRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class UserService {
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
public User findByUsername(String username) {
|
||||
return userRepository.findByUsername(username)
|
||||
.orElseThrow(() -> new UsernameNotFoundException("Usuario no encontrado: " + username));
|
||||
}
|
||||
|
||||
public List<User> findAll() {
|
||||
return userRepository.findAll();
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
public void save(User user) {
|
||||
if (user.getPassword() != null && !user.getPassword().startsWith("$2a$")) {
|
||||
user.setPassword(passwordEncoder.encode(user.getPassword()));
|
||||
}
|
||||
userRepository.save(user);
|
||||
}
|
||||
|
||||
public User findById(Long id) {
|
||||
return userRepository.findById(id).orElseThrow();
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private WeightService weightService;
|
||||
|
||||
@Transactional
|
||||
public void deleteById(Long id) {
|
||||
User user = userRepository.findById(id).orElseThrow();
|
||||
weightService.deleteByUser(user);
|
||||
userRepository.deleteById(id);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
package com.weighttracker.service;
|
||||
|
||||
import com.weighttracker.model.User;
|
||||
import com.weighttracker.model.WeightRecord;
|
||||
import com.weighttracker.repository.WeightRecordRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class WeightService {
|
||||
private final WeightRecordRepository repo;
|
||||
|
||||
public WeightService(WeightRecordRepository r) {
|
||||
this.repo = r;
|
||||
}
|
||||
|
||||
public WeightRecord save(WeightRecord w) {
|
||||
return repo.save(w);
|
||||
}
|
||||
|
||||
public List<WeightRecord> last10(User u) {
|
||||
return repo.findTop10ByUserOrderByDateDesc(u);
|
||||
}
|
||||
|
||||
public List<WeightRecord> findByUser(User u) {
|
||||
return repo.findByUserOrderByDateDesc(u);
|
||||
}
|
||||
|
||||
public List<WeightRecord> recordsAfter(User u, LocalDateTime d) {
|
||||
return repo.findByUserAndDateAfterOrderByDateAsc(u, d);
|
||||
}
|
||||
|
||||
public WeightRecord findById(Long id) {
|
||||
return repo.findById(id).orElseThrow();
|
||||
}
|
||||
|
||||
public void deleteById(Long id) {
|
||||
repo.deleteById(id);
|
||||
}
|
||||
|
||||
public List<WeightRecord> recordsBetween(User user, LocalDateTime start, LocalDateTime end) {
|
||||
return repo.findByUserAndDateBetweenOrderByDateAsc(user, start, end);
|
||||
}
|
||||
|
||||
public void deleteByUser(User user) {
|
||||
repo.deleteAllByUser(user);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user