first commit. application working

This commit is contained in:
Jaime Jiménez
2025-07-15 18:34:27 +02:00
parent bc830ef927
commit 5099ab0607
3745 changed files with 360739 additions and 3 deletions

View File

@ -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);
}
}

View File

@ -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();}
}

View File

@ -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"; }
}

View File

@ -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";
}
}

View File

@ -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";
}
}

View File

@ -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";
}
}

View 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
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}
}