Estado base antes de integración Cognito.

Incluye admin web Spring Boot, API JWT para escáner y app Flutter qrscanner con login y escaneo de pases.
This commit is contained in:
Alejandro Lara
2026-06-09 15:51:44 -06:00
commit f8468350d5
2162 changed files with 433376 additions and 0 deletions
+36
View File
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>mx.gob.slp.baseadminweb</groupId>
<artifactId>base-admin-web</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>base-admin-web-application</artifactId>
<name>base-admin-web-application</name>
<dependencies>
<dependency>
<groupId>mx.gob.slp.baseadminweb</groupId>
<artifactId>base-admin-web-domain</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,78 @@
package mx.gob.slp.baseadminweb.application.config;
import mx.gob.slp.baseadminweb.domain.model.AppUser;
import mx.gob.slp.baseadminweb.domain.model.Role;
import mx.gob.slp.baseadminweb.domain.repository.AppUserRepository;
import mx.gob.slp.baseadminweb.domain.repository.RoleRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
@Component
public class InitialDataLoader implements CommandLineRunner {
private static final String ADMIN_ROLE = "ROLE_ADMIN";
private static final String SCANNER_ROLE = "ROLE_SCANNER";
private final RoleRepository roleRepository;
private final AppUserRepository appUserRepository;
private final PasswordEncoder passwordEncoder;
private final String adminUsername;
private final String adminPassword;
private final String adminDisplayName;
private final String scannerUsername;
private final String scannerPassword;
private final String scannerDisplayName;
public InitialDataLoader(RoleRepository roleRepository,
AppUserRepository appUserRepository,
PasswordEncoder passwordEncoder,
@Value("${app.admin.username:admin}") String adminUsername,
@Value("${app.admin.password:Admin123*}") String adminPassword,
@Value("${app.admin.display-name:Administrador Base}") String adminDisplayName,
@Value("${app.scanner.username:scanner}") String scannerUsername,
@Value("${app.scanner.password:Scanner123*}") String scannerPassword,
@Value("${app.scanner.display-name:Escaneador}") String scannerDisplayName) {
this.roleRepository = roleRepository;
this.appUserRepository = appUserRepository;
this.passwordEncoder = passwordEncoder;
this.adminUsername = adminUsername;
this.adminPassword = adminPassword;
this.adminDisplayName = adminDisplayName;
this.scannerUsername = scannerUsername;
this.scannerPassword = scannerPassword;
this.scannerDisplayName = scannerDisplayName;
}
@Override
public void run(String... args) {
Role adminRole = roleRepository.findByName(ADMIN_ROLE)
.orElseGet(() -> roleRepository.save(new Role(ADMIN_ROLE, "Administrador del sistema")));
appUserRepository.findByUsername(adminUsername).orElseGet(() -> {
AppUser admin = new AppUser(
adminUsername,
passwordEncoder.encode(adminPassword),
adminDisplayName,
true
);
admin.addRole(adminRole);
return appUserRepository.save(admin);
});
Role scannerRole = roleRepository.findByName(SCANNER_ROLE)
.orElseGet(() -> roleRepository.save(new Role(SCANNER_ROLE, "Escaneo de codigos pase")));
appUserRepository.findByUsername(scannerUsername).orElseGet(() -> {
AppUser scanner = new AppUser(
scannerUsername,
passwordEncoder.encode(scannerPassword),
scannerDisplayName,
true
);
scanner.addRole(scannerRole);
return appUserRepository.save(scanner);
});
}
}
@@ -0,0 +1,23 @@
package mx.gob.slp.baseadminweb.application.service;
import mx.gob.slp.baseadminweb.domain.repository.AppUserRepository;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class AppUserDetailsService implements UserDetailsService {
private final AppUserRepository appUserRepository;
public AppUserDetailsService(AppUserRepository appUserRepository) {
this.appUserRepository = appUserRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return appUserRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("Usuario no encontrado: " + username));
}
}
@@ -0,0 +1,77 @@
package mx.gob.slp.baseadminweb.application.service;
import mx.gob.slp.baseadminweb.domain.model.PassCode;
import mx.gob.slp.baseadminweb.domain.repository.PassCodeRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.security.SecureRandom;
import java.time.Instant;
import java.util.List;
@Service
public class PassCodeService {
private static final String CODE_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
private static final int CODE_LENGTH = 10;
private static final int MAX_GENERATION_ATTEMPTS = 20;
private final PassCodeRepository passCodeRepository;
private final SecureRandom secureRandom = new SecureRandom();
public PassCodeService(PassCodeRepository passCodeRepository) {
this.passCodeRepository = passCodeRepository;
}
public List<PassCode> findAll() {
return passCodeRepository.findAll();
}
@Transactional
public PassCode create(String name) {
String trimmedName = name == null ? "" : name.trim();
if (trimmedName.isEmpty()) {
throw new IllegalArgumentException("El nombre del codigo pase es obligatorio.");
}
PassCode passCode = new PassCode(trimmedName, generateUniqueCode(), Instant.now());
return passCodeRepository.save(passCode);
}
@Transactional
public PassCode scan(String code) {
String trimmedCode = code == null ? "" : code.trim().toUpperCase();
if (trimmedCode.isEmpty()) {
throw new IllegalArgumentException("El codigo es obligatorio.");
}
PassCode passCode = passCodeRepository.findByCode(trimmedCode)
.orElseThrow(() -> new IllegalArgumentException("Codigo pase no encontrado."));
if (passCode.isScanned()) {
throw new IllegalStateException("El codigo pase ya fue escaneado.");
}
passCode.markAsUsed(Instant.now());
return passCodeRepository.save(passCode);
}
private String generateUniqueCode() {
for (int attempt = 0; attempt < MAX_GENERATION_ATTEMPTS; attempt++) {
String candidate = randomCode();
if (!passCodeRepository.existsByCode(candidate)) {
return candidate;
}
}
throw new IllegalStateException("No fue posible generar un codigo unico.");
}
private String randomCode() {
StringBuilder builder = new StringBuilder(CODE_LENGTH);
for (int i = 0; i < CODE_LENGTH; i++) {
int index = secureRandom.nextInt(CODE_ALPHABET.length());
builder.append(CODE_ALPHABET.charAt(index));
}
return builder.toString();
}
}