Add: full functionality to the core microservice

This commit is contained in:
Jose
2026-01-16 20:43:14 +01:00
parent 48f692178f
commit ba5fc38b4b
33 changed files with 965 additions and 195 deletions

View File

@@ -14,21 +14,16 @@
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="test" value="true"/>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
<attribute name="optional" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="optional" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
@@ -36,5 +31,10 @@
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
<attributes>
<attribute name="module" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -1,8 +1,13 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=25
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=25
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
org.eclipse.jdt.core.compiler.release=disabled
org.eclipse.jdt.core.compiler.source=1.8
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
org.eclipse.jdt.core.compiler.release=enabled
org.eclipse.jdt.core.compiler.source=25

View File

@@ -26,15 +26,28 @@
<attribute name="optional" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-25"/>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" path="target/generated-sources/annotations">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="ignore_optional_problems" value="true"/>
<attribute name="m2e-apt" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="ignore_optional_problems" value="true"/>
<attribute name="m2e-apt" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -20,4 +20,15 @@
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
<filteredResources>
<filter>
<id>1768579733030</id>
<name></name>
<type>30</type>
<matcher>
<id>org.eclipse.core.resources.regexFilterMatcher</id>
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
</matcher>
</filter>
</filteredResources>
</projectDescription>

View File

@@ -0,0 +1,4 @@
eclipse.preferences.version=1
org.eclipse.jdt.apt.aptEnabled=true
org.eclipse.jdt.apt.genSrcDir=target/generated-sources/annotations
org.eclipse.jdt.apt.genTestSrcDir=target/generated-test-sources/test-annotations

View File

@@ -1,8 +1,15 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.codegen.targetPlatform=25
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=25
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
org.eclipse.jdt.core.compiler.release=disabled
org.eclipse.jdt.core.compiler.source=1.8
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
org.eclipse.jdt.core.compiler.processAnnotations=enabled
org.eclipse.jdt.core.compiler.release=enabled
org.eclipse.jdt.core.compiler.source=25

View File

@@ -8,7 +8,6 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
@@ -36,6 +35,8 @@ public class JwtFilter extends OncePerRequestFilter {
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
System.out.println("JwtFilter ejecutándose para " + request.getRequestURI());
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
@@ -44,19 +45,27 @@ public class JwtFilter extends OncePerRequestFilter {
try {
if (jwtService.validateToken(token)) {
UUID userId = jwtService.getUserId(token);
short serviceId = jwtService.getServiceId(token);
Byte serviceId = jwtService.getServiceId(token);
User user = userService.getById(userId);
String roleName = switch(user.getGlobalRole()) {
case 0 -> "USER";
case 1 -> "ADMIN";
default -> "GUEST";
};
List<GrantedAuthority> authorities = List.of(
new SimpleGrantedAuthority("ROLE_" + user.getGlobalRole())
new SimpleGrantedAuthority("ROLE_" + roleName)
);
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(user, null, authorities);
auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(auth);
System.out.println("Granted Authorities: " +
SecurityContextHolder.getContext().getAuthentication().getAuthorities());
long timeLeft = jwtService.getExpiration(token).getTime() - System.currentTimeMillis();
if (timeLeft < refreshThreshold) {
String newToken = jwtService.generateToken(userId, serviceId);
@@ -68,7 +77,7 @@ public class JwtFilter extends OncePerRequestFilter {
return;
}
}
filterChain.doFilter(request, response);
}
}

View File

@@ -2,6 +2,9 @@ package net.miarma.backend.core.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
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;
@@ -12,6 +15,7 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
private final JwtFilter jwtFilter;
@@ -33,6 +37,12 @@ public class SecurityConfig {
return http.build();
}
@Bean
public AuthenticationManager authManager(HttpSecurity http) throws Exception {
return http.getSharedObject(AuthenticationManagerBuilder.class)
.build();
}
@Bean
public PasswordEncoder passwordEncoder() {

View File

@@ -4,6 +4,7 @@ import java.util.Map;
import java.util.UUID;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
@@ -11,21 +12,30 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import jakarta.validation.Valid;
import net.miarma.backend.core.dto.ChangePasswordRequest;
import net.miarma.backend.core.dto.LoginRequest;
import net.miarma.backend.core.dto.LoginResponse;
import net.miarma.backend.core.dto.RegisterRequest;
import net.miarma.backend.core.model.Credential;
import net.miarma.backend.core.service.AuthService;
import net.miarma.backend.core.service.CredentialService;
import net.miarma.backend.core.service.JwtService;
@RestController
@RequestMapping("/auth")
public class AuthController {
private final AuthService authService;
private final CredentialService credentialService;
private final JwtService jwtService;
private final PasswordEncoder passwordEncoder;
private final AuthService authService;
public AuthController(AuthService authService, JwtService jwtService) {
this.authService = authService;
public AuthController(CredentialService credentialService, JwtService jwtService,
PasswordEncoder passwordEncoder, AuthService authService) {
this.credentialService = credentialService;
this.jwtService = jwtService;
this.passwordEncoder = passwordEncoder;
this.authService = authService;
}
@PostMapping("/login")
@@ -34,6 +44,11 @@ public class AuthController {
return ResponseEntity.ok(response);
}
@PostMapping("/register")
public ResponseEntity<LoginResponse> register(@RequestBody RegisterRequest request) {
return ResponseEntity.ok(authService.register(request));
}
@PostMapping("/refresh")
public ResponseEntity<?> refreshToken(@RequestHeader("Authorization") String authHeader) {
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
@@ -46,7 +61,7 @@ public class AuthController {
}
UUID userId = jwtService.getUserId(token);
short serviceId = jwtService.getServiceId(token);
Byte serviceId = jwtService.getServiceId(token);
String newToken = jwtService.generateToken(userId, serviceId);
@@ -56,5 +71,37 @@ public class AuthController {
"serviceId", serviceId
));
}
@PostMapping("/change-password")
public ResponseEntity<?> changePassword(
@RequestHeader("Authorization") String authHeader,
@Valid @RequestBody ChangePasswordRequest request
) {
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
return ResponseEntity.status(401).body("Token missing");
}
String token = authHeader.substring(7);
if (!jwtService.validateToken(token)) {
return ResponseEntity.status(401).body("Invalid token");
}
UUID userId = jwtService.getUserId(token);
Credential cred = credentialService.getByUserId(userId)
.stream()
.filter(c -> c.getServiceId().equals(request.getServiceId()))
.findFirst().get();
if (cred == null) {
return ResponseEntity.status(404).body("Credential not found");
}
if (!passwordEncoder.matches(request.getOldPassword(), cred.getPassword())) {
return ResponseEntity.status(400).body("Old password is incorrect");
}
credentialService.updatePassword(cred.getCredentialId(), request);
return ResponseEntity.ok(Map.of("message", "Password changed successfully"));
}
}

View File

@@ -1,5 +1,64 @@
package net.miarma.backend.core.controller;
import java.util.List;
import java.util.UUID;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import net.miarma.backend.core.dto.CredentialDto;
import net.miarma.backend.core.model.Credential;
import net.miarma.backend.core.service.CredentialService;
@RestController
@RequestMapping("/credentials")
public class CredentialController {
private final CredentialService credentialService;
public CredentialController(CredentialService credentialService) {
this.credentialService = credentialService;
}
@GetMapping
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<List<Credential>> getAll() {
return ResponseEntity.ok(credentialService.getAll());
}
@GetMapping("/user/{userId}")
@PreAuthorize("hasRole('ADMIN') or #userId == authentication.principal.userId")
public ResponseEntity<List<Credential>> getByUserId(@PathVariable("userId") UUID userId) {
return ResponseEntity.ok(credentialService.getByUserId(userId));
}
@GetMapping("/{credentialId}")
@PreAuthorize("hasRole('ADMIN') or @credentialService.isOwner(#credentialId, authentication.principal.userId)")
public ResponseEntity<Credential> getById(@PathVariable("credentialId") UUID credentialId) {
return ResponseEntity.ok(credentialService.getById(credentialId));
}
@PutMapping("/{credentialId}")
@PreAuthorize("hasRole('ADMIN') or @credentialService.isOwner(#credentialId, authentication.principal.userId)")
public ResponseEntity<Credential> update(
@PathVariable("credentialId") UUID credentialId,
@RequestBody CredentialDto dto
) {
dto.setCredentialId(credentialId);
return ResponseEntity.ok(credentialService.update(credentialId, dto));
}
@DeleteMapping("/{credentialId}")
@PreAuthorize("hasRole('ADMIN') or @credentialService.isOwner(#credentialId, authentication.principal.userId)")
public ResponseEntity<Void> delete(@PathVariable("credentialId") UUID credentialId) {
credentialService.delete(credentialId);
return ResponseEntity.noContent().build();
}
}

View File

@@ -1,5 +1,87 @@
package net.miarma.backend.core.controller;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import net.miarma.backend.core.model.File;
import net.miarma.backend.core.service.FileService;
@RestController
@RequestMapping("/files")
public class FileController {
private final FileService fileService;
public FileController(FileService fileService) {
this.fileService = fileService;
}
@GetMapping
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<List<File>> getAll(@RequestParam Map<String,String> params) {
List<File> files = fileService.getAll(params);
return ResponseEntity.ok(files);
}
@GetMapping("/{fileId}")
@PreAuthorize("hasRole('ADMIN') or @fileService.isOwner(#fileId, authentication.principal.userId)")
public ResponseEntity<File> getById(@PathVariable UUID fileId) {
File file = fileService.getById(fileId);
return ResponseEntity.ok(file);
}
@PostMapping
@PreAuthorize("hasRole('ADMIN') or #uploadedBy == authentication.principal.userId")
public ResponseEntity<File> create(
@RequestParam String fileName,
@RequestParam String mimeType,
@RequestParam UUID uploadedBy,
@RequestParam Short context,
@RequestPart("file") MultipartFile file
) throws IOException {
File entity = new File();
entity.setFileName(fileName);
entity.setMimeType(mimeType);
entity.setUploadedBy(uploadedBy);
entity.setContext(context);
File created = fileService.create(entity, file.getBytes());
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}
@PutMapping("/{fileId}")
@PreAuthorize("hasRole('ADMIN') or @fileService.isOwner(#fileId, authentication.principal.userId)")
public ResponseEntity<File> update(@PathVariable UUID fileId, @RequestBody File file) {
file.setFileId(fileId);
File updated = fileService.update(file);
return ResponseEntity.ok(updated);
}
@DeleteMapping("/{fileId}")
@PreAuthorize("hasRole('ADMIN') or @fileService.isOwner(#fileId, authentication.principal.userId)")
public ResponseEntity<Void> delete(@PathVariable UUID fileId, @RequestBody Map<String,String> body) throws IOException {
String filePath = body.get("file_path");
Files.deleteIfExists(Paths.get(filePath));
fileService.delete(fileId);
return ResponseEntity.noContent().build();
}
}

View File

@@ -1,10 +1,80 @@
package net.miarma.backend.core.controller;
import java.util.List;
import java.util.UUID;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import net.miarma.backend.core.dto.UserDto;
import net.miarma.backend.core.model.User;
import net.miarma.backend.core.service.JwtService;
import net.miarma.backend.core.service.UserService;
@RestController
@RequestMapping("/users")
public class UserController {
private UserService userService;
private JwtService jwtService;
public UserController(UserService userService, JwtService jwtService) {
this.userService = userService;
this.jwtService = jwtService;
}
@GetMapping
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<List<UserDto>> getAll() {
return ResponseEntity.ok(
userService.getAll()
.stream()
.map(UserDto::fromEntity)
.toList()
);
}
@GetMapping("/{user_id}")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<UserDto> getById(@PathVariable("user_id") UUID userId) {
User user = userService.getById(userId);
return ResponseEntity.ok(UserDto.fromEntity(user));
}
@PutMapping("/{user_id}")
@PreAuthorize("hasRole('ADMIN') or #userId == principal.userId")
public ResponseEntity<UserDto> update(@PathVariable("user_id") UUID userId, @RequestBody UserDto dto) {
return ResponseEntity.ok(UserDto.fromEntity(userService.update(userId, dto)));
}
@GetMapping("/{user_id}/avatar")
public ResponseEntity<String> getAvatar(@PathVariable("user_id") UUID userId) {
return ResponseEntity.ok(userService.getById(userId).getAvatar());
}
@GetMapping("/me")
public ResponseEntity<UserDto> getMe(@RequestHeader("Authorization") String authHeader) {
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
String token = authHeader.substring(7);
UUID userId;
try {
userId = jwtService.getUserId(token);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
User user = userService.getById(userId);
return ResponseEntity.ok(UserDto.fromEntity(user));
}
}

View File

@@ -0,0 +1,45 @@
package net.miarma.backend.core.dto;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
public class ChangePasswordRequest {
@NotBlank
private String oldPassword;
@NotBlank
private String newPassword;
@NotNull
@Min(0)
private Byte serviceId;
public String getOldPassword() {
return oldPassword;
}
public void setOldPassword(String oldPassword) {
this.oldPassword = oldPassword;
}
public String getNewPassword() {
return newPassword;
}
public void setNewPassword(String newPassword) {
this.newPassword = newPassword;
}
public Byte getServiceId() {
return serviceId;
}
public void setServiceId(Byte serviceId) {
this.serviceId = serviceId;
}
}

View File

@@ -0,0 +1,89 @@
package net.miarma.backend.core.dto;
import java.time.Instant;
import java.util.UUID;
import net.miarma.backend.core.model.Credential;
public class CredentialDto {
private UUID credentialId;
private Byte serviceId;
private String username;
private String email;
private Byte status;
private Instant createdAt;
private Instant updatedAt;
public CredentialDto() {}
public CredentialDto(Credential credential) {
this.credentialId = credential.getCredentialId();
this.serviceId = credential.getServiceId();
this.username = credential.getUsername();
this.email = credential.getEmail();
this.status = credential.getStatus();
this.createdAt = credential.getCreatedAt();
this.updatedAt = credential.getUpdatedAt();
}
public static CredentialDto fromEntity(Credential credential) {
if (credential == null) return null;
return new CredentialDto(credential);
}
public UUID getCredentialId() {
return credentialId;
}
public void setCredentialId(UUID credentialId) {
this.credentialId = credentialId;
}
public Byte getServiceId() {
return serviceId;
}
public void setServiceId(Byte serviceId) {
this.serviceId = serviceId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Byte getStatus() {
return status;
}
public void setStatus(Byte status) {
this.status = status;
}
public Instant getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Instant createdAt) {
this.createdAt = createdAt;
}
public Instant getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Instant updatedAt) {
this.updatedAt = updatedAt;
}
}

View File

@@ -9,7 +9,7 @@ public class LoginRequest {
@NotBlank
private String password;
private short serviceId;
private Byte serviceId;
public String getUsername() {
return username;
@@ -27,11 +27,11 @@ public class LoginRequest {
this.password = password;
}
public short getServiceId() {
public Byte getServiceId() {
return serviceId;
}
public void setServiceId(short serviceId) {
public void setServiceId(Byte serviceId) {
this.serviceId = serviceId;
}
}

View File

@@ -2,13 +2,13 @@ package net.miarma.backend.core.dto;
public class LoginResponse {
private String token;
private short serviceId;
private UserDto user;
private CredentialDto account;
public LoginResponse(String token, short serviceId, UserDto user) {
public LoginResponse(String token, UserDto user, CredentialDto account) {
this.token = token;
this.serviceId = serviceId;
this.user = user;
this.account = account;
}
public String getToken() {
@@ -19,14 +19,6 @@ public class LoginResponse {
this.token = token;
}
public short getServiceId() {
return serviceId;
}
public void setServiceId(short serviceId) {
this.serviceId = serviceId;
}
public UserDto getUser() {
return user;
}
@@ -34,4 +26,12 @@ public class LoginResponse {
public void setUser(UserDto user) {
this.user = user;
}
public CredentialDto getAccount() {
return account;
}
public void setAccount(CredentialDto account) {
this.account = account;
}
}

View File

@@ -0,0 +1,41 @@
package net.miarma.backend.core.dto;
public class RegisterRequest {
private String username;
private String email;
private String password;
private String displayName;
private Byte serviceId;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
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;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public Byte getServiceId() {
return serviceId;
}
public void setServiceId(Byte serviceId) {
this.serviceId = serviceId;
}
}

View File

@@ -3,6 +3,8 @@ package net.miarma.backend.core.dto;
import java.time.Instant;
import java.util.UUID;
import net.miarma.backend.core.model.User;
public class UserDto {
private UUID userId;
private String displayName;
@@ -12,6 +14,8 @@ public class UserDto {
private Instant createdAt;
private Instant updatedAt;
public UserDto() {}
public UserDto(UUID userId, String displayName, String avatar, Byte globalStatus, Byte globalRole,
Instant createdAt, Instant updatedAt) {
this.userId = userId;
@@ -23,6 +27,20 @@ public class UserDto {
this.updatedAt = updatedAt;
}
public static UserDto fromEntity(User user) {
if (user == null) return null;
return new UserDto(
user.getUserId(),
user.getDisplayName(),
user.getAvatar(),
user.getGlobalStatus(),
user.getGlobalRole(),
user.getCreatedAt(),
user.getUpdatedAt()
);
}
public UUID getUserId() {
return userId;
}

View File

@@ -3,10 +3,8 @@ package net.miarma.backend.core.model;
import java.nio.ByteBuffer;
import java.time.Instant;
import java.util.UUID;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
@@ -21,81 +19,83 @@ import jakarta.persistence.Transient;
import jakarta.persistence.UniqueConstraint;
@Entity
@Table(
name = "credentials",
uniqueConstraints = {
@UniqueConstraint(columnNames = {"service_id", "username"}),
@UniqueConstraint(columnNames = {"service_id", "email"})
}
)
@Table(name = "credentials",
uniqueConstraints = {
@UniqueConstraint(columnNames = { "service_id", "username" }),
@UniqueConstraint(columnNames = { "service_id", "email" })
})
public class Credential {
@Id
@Column(columnDefinition = "BINARY(16)")
private byte[] credentialIdBin;
@Column(name = "credential_id", columnDefinition = "BINARY(16)")
private byte[] credentialIdBin;
@Column(name = "user_id", columnDefinition = "BINARY(16)")
private byte[] userIdBin;
@Transient
private UUID credentialId;
@Transient
private UUID userId;
@Column(name = "service_id")
private Byte serviceId;
private String username;
private String email;
private String password;
private Byte status;
@CreationTimestamp
private Instant createdAt;
@UpdateTimestamp
private Instant updatedAt;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", insertable = false, updatable = false)
private User user;
@Column(name = "user_id", columnDefinition = "BINARY(16)")
private byte[] userIdBin;
@Transient
private UUID credentialId;
@PrePersist
@PreUpdate
private void prePersist() {
if (credentialId != null) {
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(credentialId.getMostSignificantBits());
bb.putLong(credentialId.getLeastSignificantBits());
credentialIdBin = bb.array();
}
if (userId != null) {
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(userId.getMostSignificantBits());
bb.putLong(userId.getLeastSignificantBits());
userIdBin = bb.array();
}
}
@Transient
private UUID userId;
@Column(name = "service_id")
private Byte serviceId;
private String username;
private String email;
private String password;
private Byte status;
@CreationTimestamp
private Instant createdAt;
@UpdateTimestamp
private Instant updatedAt;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", insertable = false, updatable = false)
private User user;
@PrePersist
@PreUpdate
private void prePersist() {
if (credentialId != null) {
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(credentialId.getMostSignificantBits());
bb.putLong(credentialId.getLeastSignificantBits());
credentialIdBin = bb.array();
}
if (userId != null) {
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(userId.getMostSignificantBits());
bb.putLong(userId.getLeastSignificantBits());
userIdBin = bb.array();
}
}
@PostLoad
private void postLoad() {
if (credentialIdBin != null) {
ByteBuffer bb = ByteBuffer.wrap(credentialIdBin);
long high = bb.getLong();
long low = bb.getLong();
credentialId = new UUID(high, low);
}
if (userIdBin != null) {
ByteBuffer bb = ByteBuffer.wrap(userIdBin);
long high = bb.getLong();
long low = bb.getLong();
userId = new UUID(high, low);
}
}
@PostLoad
private void postLoad() {
if (credentialIdBin != null) {
ByteBuffer bb = ByteBuffer.wrap(credentialIdBin);
long high = bb.getLong();
long low = bb.getLong();
credentialId = new UUID(high, low);
}
if (userIdBin != null) {
ByteBuffer bb = ByteBuffer.wrap(userIdBin);
long high = bb.getLong();
long low = bb.getLong();
userId = new UUID(high, low);
}
}
public UUID getCredentialId() {
if (credentialId == null && credentialIdBin != null) {
ByteBuffer bb = ByteBuffer.wrap(credentialIdBin);
credentialId = new UUID(bb.getLong(), bb.getLong());
}
return credentialId;
}
@@ -174,4 +174,4 @@ public class Credential {
public void setUser(User user) {
this.user = user;
}
}
}

View File

@@ -5,18 +5,35 @@ import java.util.Optional;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import net.miarma.backend.core.model.Credential;
public interface CredentialRepository extends JpaRepository<Credential, UUID> {
public interface CredentialRepository extends JpaRepository<Credential, byte[]> {
Optional<Credential> findByServiceIdAndUsername(short serviceId, String username);
@Query("""
SELECT c FROM Credential c
JOIN FETCH c.user
WHERE c.serviceId = :serviceId
AND c.username = :username
""")
Optional<Credential> findByServiceIdAndUsername(@Param("serviceId") Byte serviceId,
@Param("username") String username);
Optional<Credential> findByServiceIdAndEmail(short serviceId, String email);
Optional<Credential> findByServiceIdAndEmail(Byte serviceId, String email);
Optional<Credential> findByUserIdAndServiceId(UUID userId, short serviceId);
Optional<Credential> findByUserIdAndServiceId(UUID userId, Byte serviceId);
List<Credential> findByUserId(UUID userId);
Optional<Credential> findByUsernameAndServiceId(String username, int serviceId);
@Query("SELECT c FROM Credential c WHERE c.userIdBin = :userIdBin")
List<Credential> findByUserId(@Param("userIdBin") byte[] userIdBin);
Optional<Credential> findByEmail(String email);
boolean existsByUsernameAndServiceId(String username, Byte serviceId);
boolean existsByEmailAndServiceId(String email, Byte serviceId);
}

View File

@@ -1,17 +1,13 @@
package net.miarma.backend.core.repository;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import net.miarma.backend.core.model.File;
public interface FileRepository extends JpaRepository<File, UUID> {
Optional<File> findById(UUID fileId);
public interface FileRepository extends JpaRepository<File, byte[]> {
List<File> findByUploadedBy(UUID uploadedBy);
List<File> findByContext(short context);

View File

@@ -1,11 +1,9 @@
package net.miarma.backend.core.repository;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import net.miarma.backend.core.model.User;
public interface UserRepository extends JpaRepository<User, UUID> {
public interface UserRepository extends JpaRepository<User, byte[]> {
}

View File

@@ -1,46 +1,76 @@
package net.miarma.backend.core.service;
import java.util.UUID;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import net.miarma.backend.core.dto.CredentialDto;
import net.miarma.backend.core.dto.LoginRequest;
import net.miarma.backend.core.dto.LoginResponse;
import net.miarma.backend.core.dto.RegisterRequest;
import net.miarma.backend.core.dto.UserDto;
import net.miarma.backend.core.model.Credential;
import net.miarma.backend.core.model.User;
@Service
public class AuthService {
private final CredentialService credentialService;
private final UserService userService;
private final JwtService jwtService;
private final PasswordEncoder passwordEncoder;
public AuthService(CredentialService credentialService, JwtService jwtService,
PasswordEncoder passwordEncoder) {
public AuthService(CredentialService credentialService, UserService userService,
JwtService jwtService, PasswordEncoder passwordEncoder) {
this.credentialService = credentialService;
this.userService = userService;
this.jwtService = jwtService;
this.passwordEncoder = passwordEncoder;
}
public LoginResponse login(LoginRequest request) {
Credential cred = credentialService.getByUserIdAndService(request.getServiceId(), request.getUsername());
if (!passwordEncoder.matches(request.getPassword(), cred.getPassword())) {
throw new RuntimeException("Invalid credentials");
}
String token = jwtService.generateToken(cred.getUserId(), request.getServiceId());
UserDto userDto = new UserDto(
cred.getUser().getUserId(),
cred.getUser().getDisplayName(),
cred.getUser().getAvatar(),
cred.getUser().getGlobalStatus(),
cred.getUser().getGlobalRole(),
cred.getUser().getCreatedAt(),
cred.getUser().getUpdatedAt()
);
UserDto userDto = UserDto.fromEntity(cred.getUser());
CredentialDto credentialDto = CredentialDto.fromEntity(cred);
return new LoginResponse(token, request.getServiceId(), userDto);
return new LoginResponse(token, userDto, credentialDto);
}
public LoginResponse register(RegisterRequest request) {
if (credentialService.existsByUsernameAndService(request.getUsername(), request.getServiceId())) {
throw new RuntimeException("Username already taken");
}
User user;
try {
user = credentialService.getByEmail(request.getEmail());
} catch (RuntimeException e) {
UserDto dto = new UserDto();
dto.setUserId(UUID.randomUUID());
dto.setDisplayName(request.getDisplayName());
user = userService.create(dto);
}
Credential cred = new Credential();
cred.setCredentialId(UUID.randomUUID());
cred.setUser(user);
cred.setServiceId((byte) request.getServiceId());
cred.setUsername(request.getUsername());
cred.setEmail(request.getEmail());
cred.setPassword(passwordEncoder.encode(request.getPassword()));
credentialService.create(cred);
String token = jwtService.generateToken(user.getUserId(), request.getServiceId());
return new LoginResponse(token, UserDto.fromEntity(user), CredentialDto.fromEntity(cred));
}
}

View File

@@ -3,47 +3,143 @@ package net.miarma.backend.core.service;
import java.util.List;
import java.util.UUID;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import net.miarma.backend.core.dto.ChangePasswordRequest;
import net.miarma.backend.core.dto.CredentialDto;
import net.miarma.backend.core.model.Credential;
import net.miarma.backend.core.model.User;
import net.miarma.backend.core.repository.CredentialRepository;
import net.miarma.backend.core.util.UuidUtils;
@Service
@Transactional
public class CredentialService {
private final CredentialRepository credentialRepository;
private final PasswordEncoder passwordEncoder;
public CredentialService(CredentialRepository credentialRepository) {
public CredentialService(CredentialRepository credentialRepository, PasswordEncoder passwordEncoder) {
this.credentialRepository = credentialRepository;
this.passwordEncoder = passwordEncoder;
}
public Credential getById(UUID id) {
return credentialRepository.findById(id)
public Credential getById(UUID credentialId) {
byte[] idBytes = UuidUtils.uuidToBin(credentialId);
return credentialRepository.findById(idBytes)
.orElseThrow(() -> new RuntimeException("Credential not found"));
}
public Credential create(Credential credential) {
// TODO: validate duplicates here
if (credential.getUsername() == null || credential.getUsername().isBlank()) {
throw new IllegalArgumentException("Username cannot be blank");
}
if (credential.getEmail() == null || !credential.getEmail().matches("^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$")) {
throw new IllegalArgumentException("Invalid email format");
}
if (credential.getPassword() == null || credential.getPassword().length() < 6) {
throw new IllegalArgumentException("Password must be at least 6 characters");
}
if (credential.getServiceId() == null || credential.getServiceId() < 0) {
throw new IllegalArgumentException("ServiceId must be positive");
}
boolean existsUsername = credentialRepository.existsByUsernameAndServiceId(
credential.getUsername(), credential.getServiceId());
if (existsUsername) throw new IllegalArgumentException("Username already exists for this service");
boolean existsEmail = credentialRepository.existsByEmailAndServiceId(
credential.getEmail(), credential.getServiceId());
if (existsEmail) throw new IllegalArgumentException("Email already exists for this service");
credential.setPassword(passwordEncoder.encode(credential.getPassword()));
return credentialRepository.save(credential);
}
public List<Credential> getAll() {
return credentialRepository.findAll();
}
public List<Credential> getByUserId(UUID userId) {
List<Credential> creds = credentialRepository.findByUserId(userId);
List<Credential> creds = credentialRepository.findByUserId(UuidUtils.uuidToBin(userId));
if (creds.isEmpty()) {
throw new RuntimeException("User has no credentials");
}
return creds;
}
public Credential getByUserIdAndService(short serviceId, String username) {
public User getByEmail(String email) {
return credentialRepository.findByEmail(email)
.orElseThrow(() -> new RuntimeException("No credential found for email"))
.getUser();
}
public Credential getByUserIdAndService(Byte serviceId, String username) {
return credentialRepository.findByServiceIdAndUsername(serviceId, username)
.orElseThrow(() -> new RuntimeException("Credential not found in this site"));
}
public Credential getForLogin(short serviceId, String username) {
public Credential getForLogin(Byte serviceId, String username) {
return credentialRepository.findByServiceIdAndUsername(serviceId, username)
.orElseThrow(() -> new RuntimeException("Invalid credentials"));
}
public boolean existsByUsernameAndService(String username, int serviceId) {
return credentialRepository.findByUsernameAndServiceId(username, serviceId).isPresent();
}
public boolean isOwner(UUID credentialId, UUID userId) {
byte[] idBytes = UuidUtils.uuidToBin(credentialId);
Credential c = credentialRepository.findById(idBytes)
.orElseThrow(() -> new RuntimeException("Credential not found"));
return c.getUserId().equals(userId);
}
public Credential update(UUID credentialId, CredentialDto dto) {
byte[] idBytes = UuidUtils.uuidToBin(credentialId);
Credential cred = credentialRepository.findById(idBytes)
.orElseThrow(() -> new RuntimeException("Credential not found"));
if (dto.getUsername() != null && dto.getUsername().isBlank()) {
throw new IllegalArgumentException("Username cannot be blank");
}
if (dto.getEmail() != null && !dto.getEmail().matches("^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$")) {
throw new IllegalArgumentException("Invalid email format");
}
if (dto.getServiceId() != null && dto.getServiceId() < 0) {
throw new IllegalArgumentException("ServiceId must be positive");
}
if (dto.getUsername() != null) cred.setUsername(dto.getUsername());
if (dto.getEmail() != null) cred.setEmail(dto.getEmail());
if (dto.getServiceId() != null) cred.setServiceId(dto.getServiceId());
if (dto.getStatus() != null) cred.setStatus(dto.getStatus());
return credentialRepository.save(cred);
}
public Credential updatePassword(UUID credentialId, ChangePasswordRequest request) {
byte[] idBytes = UuidUtils.uuidToBin(credentialId);
Credential cred = credentialRepository.findById(idBytes)
.orElseThrow(() -> new RuntimeException("Credential not found"));
if (!passwordEncoder.matches(request.getOldPassword(), cred.getPassword())) {
throw new IllegalArgumentException("Old password is incorrect");
}
cred.setPassword(passwordEncoder.encode(request.getNewPassword()));
return credentialRepository.save(cred);
}
public void delete(UUID credentialId) {
byte[] idBytes = UuidUtils.uuidToBin(credentialId);
if(!credentialRepository.existsById(idBytes))
throw new RuntimeException("Credential not found");
credentialRepository.deleteById(idBytes);
}
}

View File

@@ -1,6 +1,12 @@
package net.miarma.backend.core.service;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.springframework.stereotype.Service;
@@ -8,39 +14,79 @@ import org.springframework.transaction.annotation.Transactional;
import net.miarma.backend.core.model.File;
import net.miarma.backend.core.repository.FileRepository;
import net.miarma.backend.core.util.UuidUtils;
@Service
@Transactional
public class FileService {
private final FileRepository fileRepository;
private final String basePath = "/var/www/files";
public FileService(FileRepository fileRepository) {
this.fileRepository = fileRepository;
}
public File get(UUID fileId) {
return fileRepository.findById(fileId)
public File getById(UUID fileId) {
byte[] idBytes = UuidUtils.uuidToBin(fileId);
return fileRepository.findById(idBytes)
.orElseThrow(() -> new RuntimeException("File not found"));
}
public List<File> listByUser(UUID userId) {
public List<File> getAll(Map<String,String> params) {
if (params.containsKey("userId")) {
UUID userId = UUID.fromString(params.get("userId"));
return fileRepository.findByUploadedBy(userId);
}
if (params.containsKey("context")) {
short context = Short.parseShort(params.get("context"));
return fileRepository.findByContext(context);
}
return fileRepository.findAll();
}
public List<File> getByUserId(UUID userId) {
return fileRepository.findByUploadedBy(userId);
}
public List<File> listByContext(short context) {
return fileRepository.findByContext(context);
}
public File create(File file, byte[] fileBinary) throws IOException {
Path dirPath = Paths.get(basePath, String.valueOf(file.getContext()));
if (!Files.exists(dirPath)) {
Files.createDirectories(dirPath);
}
Path filePath = dirPath.resolve(file.getFileName());
try (FileOutputStream fos = new FileOutputStream(filePath.toFile())) {
fos.write(fileBinary);
}
file.setFilePath(filePath.toString());
public File create(File file) {
return fileRepository.save(file);
}
public void delete(UUID fileId) {
if (!fileRepository.existsById(fileId)) {
public File update(File file) {
byte[] idBytes = UuidUtils.uuidToBin(file.getFileId());
if (!fileRepository.existsById(idBytes)) {
throw new RuntimeException("File not found");
}
fileRepository.deleteById(fileId);
return fileRepository.save(file);
}
public void delete(UUID fileId) {
byte[] idBytes = UuidUtils.uuidToBin(fileId);
if (!fileRepository.existsById(idBytes)) {
throw new RuntimeException("File not found");
}
fileRepository.deleteById(idBytes);
}
public boolean isOwner(UUID fileId, UUID userId) {
byte[] fileBytes = UuidUtils.uuidToBin(fileId);
return fileRepository.findById(fileBytes)
.map(f -> f.getUploadedBy().equals(userId))
.orElse(false);
}
}

View File

@@ -16,7 +16,7 @@ public class JwtService {
private final long expiration = 3600_000;
public String generateToken(UUID userId, short serviceId) {
public String generateToken(UUID userId, Byte serviceId) {
Date now = new Date();
Date exp = new Date(now.getTime() + expiration);
@@ -43,9 +43,9 @@ public class JwtService {
return UUID.fromString(claims.getSubject());
}
public short getServiceId(String token) {
public Byte getServiceId(String token) {
Claims claims = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
return ((Number) claims.get("service")).shortValue();
return ((Number) claims.get("service")).byteValue();
}
public Date getExpiration(String token) {

View File

@@ -1,12 +1,15 @@
package net.miarma.backend.core.service;
import java.util.List;
import java.util.UUID;
import org.springframework.stereotype.Service;
import jakarta.transaction.Transactional;
import net.miarma.backend.core.dto.UserDto;
import net.miarma.backend.core.model.User;
import net.miarma.backend.core.repository.UserRepository;
import net.miarma.backend.core.util.UuidUtils;
@Service
@Transactional
@@ -17,25 +20,59 @@ public class UserService {
this.userRepository = userRepository;
}
public List<User> getAll() {
return userRepository.findAll();
}
public User getById(UUID userId) {
return userRepository.findById(userId)
.orElseThrow(() -> new RuntimeException("User not found"));
byte[] idBytes = UuidUtils.uuidToBin(userId);
return userRepository.findById(idBytes)
.orElseThrow(() -> new RuntimeException("User not found"));
}
public User create(User user) {
// TODO: basic validation
return userRepository.save(user);
}
public User update(User user) {
if(!userRepository.existsById(user.getUserId()))
throw new RuntimeException("User not found");
return userRepository.save(user);
}
public User create(UserDto dto) {
if(dto.getDisplayName() == null || dto.getDisplayName().isBlank()) {
throw new RuntimeException("Display name is required");
}
User user = new User();
user.setUserId(UUID.randomUUID());
user.setDisplayName(dto.getDisplayName());
user.setAvatar(dto.getAvatar());
user.setGlobalRole(dto.getGlobalRole() != null ? dto.getGlobalRole() : 0);
user.setGlobalStatus(dto.getGlobalStatus() != null ? dto.getGlobalStatus() : 1);
return userRepository.save(user);
}
public User update(UUID userId, UserDto dto) {
byte[] idBytes = UuidUtils.uuidToBin(userId);
User user = userRepository.findById(idBytes)
.orElseThrow(() -> new RuntimeException("User not found"));
if (dto.getDisplayName() != null) {
String displayName = dto.getDisplayName().trim();
if (displayName.isEmpty()) {
throw new IllegalArgumentException("Display name cannot be empty");
}
if (displayName.length() > 50) {
throw new IllegalArgumentException("Display name too long (max 50 chars)");
}
user.setDisplayName(displayName);
}
if(dto.getDisplayName() != null) user.setDisplayName(dto.getDisplayName());
if(dto.getAvatar() != null) user.setAvatar(dto.getAvatar());
if(dto.getGlobalRole() != null) user.setGlobalRole(dto.getGlobalRole());
if(dto.getGlobalStatus() != null) user.setGlobalStatus(dto.getGlobalStatus());
return userRepository.save(user);
}
public void delete(UUID userId) {
if(!userRepository.existsById(userId))
byte[] idBytes = UuidUtils.uuidToBin(userId);
if(!userRepository.existsById(idBytes))
throw new RuntimeException("User not found");
userRepository.deleteById(userId);
userRepository.deleteById(idBytes);
}
}

View File

@@ -0,0 +1,21 @@
package net.miarma.backend.core.util;
import java.nio.ByteBuffer;
import java.util.UUID;
public class UuidUtils {
public static byte[] uuidToBin(UUID uuid) {
ByteBuffer bb = ByteBuffer.allocate(16);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
return bb.array();
}
public static UUID binToUUID(byte[] bin) {
ByteBuffer bb = ByteBuffer.wrap(bin);
long high = bb.getLong();
long low = bb.getLong();
return new UUID(high, low);
}
}

View File

@@ -1,12 +1,47 @@
server:
port: 8080
servlet:
context-path: /v2/core
spring:
application:
name: core-service
datasource:
url: jdbc:mariadb://localhost:3306/miarma_v2
username: admin
password: ositovito
driver-class-name: org.mariadb.jdbc.Driver
jpa:
open-in-view: false
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
format_sql: true
jdbc:
time_zone: UTC
dialect: org.hibernate.dialect.MariaDBDialect
jackson:
serialization:
INDENT_OUTPUT: true
date-format: yyyy-MM-dd'T'HH:mm:ss
time-zone: Europe/Madrid
default-property-inclusion: non_null
generator:
WRITE_DATES_AS_TIMESTAMPS: false
logging:
level:
org.hibernate.SQL: DEBUG
org.hibernate.orm.jdbc.bind: TRACE
org.springframework.security: INFO
management:
endpoints:
web:
exposure:
include: health,info

1
target/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/test-classes/

View File

@@ -1,4 +0,0 @@
Manifest-Version: 1.0
Build-Jdk-Spec: 21
Created-By: Maven Integration for Eclipse

View File

@@ -1,7 +0,0 @@
#Generated by Maven Integration for Eclipse
#Fri Jan 16 02:26:12 CET 2026
artifactId=backend
groupId=net.miarma
m2e.projectLocation=/home/jomaa/git/miarma-backend
m2e.projectName=backend
version=1.0.0

View File

@@ -1,6 +0,0 @@
<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>
<groupId>net.miarma</groupId>
<artifactId>backend</artifactId>
<version>1.0.0</version>
</project>