Verschiebung nach anderem RePo - nun pro Projekt getrennt

This commit is contained in:
2026-04-01 10:41:19 +02:00
commit 7b9eda1d62
1048 changed files with 93351 additions and 0 deletions

View File

@@ -0,0 +1,442 @@
package de.oaa.xxx.user;
import java.security.Principal;
import java.time.LocalDate;
import java.time.Period;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseCookie;
import org.springframework.http.ResponseEntity;
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.RestController;
import de.oaa.xxx.games.bdsm.entity.BdsmDefaultsEntity;
import de.oaa.xxx.games.bdsm.repository.BdsmDefaultsRepository;
import de.oaa.xxx.games.chastity.common.BaseLockRepository;
import de.oaa.xxx.games.chastity.common.BaseLockTemplateRepository;
import de.oaa.xxx.games.chastity.common.CodeCreator;
import de.oaa.xxx.games.chastity.ttlock.TTAuthService;
import de.oaa.xxx.games.chastity.ttlock.TTLockConfigRepository;
import de.oaa.xxx.games.chastity.ttlock.TTLockService;
import de.oaa.xxx.games.chastity.ttlock.TTLockUserConfigEntity;
import de.oaa.xxx.games.chastity.ttlock.TTLockUserConfigRepository;
import de.oaa.xxx.registration.Registration;
import de.oaa.xxx.registration.RegistrationRepository;
import de.oaa.xxx.social.entity.MessageCause;
import de.oaa.xxx.social.entity.NotificationPreferenceEntity;
import de.oaa.xxx.social.repository.NotificationPreferenceRepository;
import org.springframework.util.DigestUtils;
import java.nio.charset.StandardCharsets;
@RestController
@RequestMapping("/user")
public class UserController {
private static final Logger LOGGER = LoggerFactory.getLogger(UserController.class);
private final UserRepository userRepository;
private final RegistrationRepository registrationRepository;
private final NotificationPreferenceRepository notificationPreferenceRepository;
private final BdsmDefaultsRepository bdsmDefaultsRepository;
private final UserService userService;
private final TTLockUserConfigRepository ttLockUserConfigRepository;
private final TTLockConfigRepository ttLockConfigRepository;
private final TTAuthService ttAuthService;
private final TTLockService ttLockService;
private final BaseLockRepository baseLockRepository;
private final BaseLockTemplateRepository baseLockTemplateRepository;
public UserController(UserRepository userRepository,
RegistrationRepository registrationRepository,
NotificationPreferenceRepository notificationPreferenceRepository,
BdsmDefaultsRepository bdsmDefaultsRepository,
UserService userService,
TTLockUserConfigRepository ttLockUserConfigRepository,
TTLockConfigRepository ttLockConfigRepository,
TTAuthService ttAuthService,
TTLockService ttLockService,
BaseLockRepository baseLockRepository,
BaseLockTemplateRepository baseLockTemplateRepository) {
this.userRepository = userRepository;
this.registrationRepository = registrationRepository;
this.notificationPreferenceRepository = notificationPreferenceRepository;
this.bdsmDefaultsRepository = bdsmDefaultsRepository;
this.userService = userService;
this.ttLockUserConfigRepository = ttLockUserConfigRepository;
this.ttLockConfigRepository = ttLockConfigRepository;
this.ttAuthService = ttAuthService;
this.ttLockService = ttLockService;
this.baseLockRepository = baseLockRepository;
this.baseLockTemplateRepository = baseLockTemplateRepository;
}
record ProfilePictureRequest(String picture, String pictureHq) {}
record NameChangeRequest(String name) {}
record GeburtsdatumChangeRequest(LocalDate geburtsdatum) {}
record TtlockUserConfigDto(String username, boolean passwordSet, Integer lockId, boolean testSuccessful) {}
record TtlockUserConfigRequest(String username, String password, Integer lockId) {}
record ProfileRequest(Integer groesse, Integer gewicht,
Geschlecht geschlecht, Neigung neigung, Beziehungsstatus beziehungsstatus, String beschreibung) {}
record PrivacyRequest(
Sichtbarkeit sichtbarkeitGrunddaten,
Sichtbarkeit sichtbarkeitGalerie,
Sichtbarkeit sichtbarkeitFreunde,
Sichtbarkeit sichtbarkeitFeed,
Sichtbarkeit sichtbarkeitPinnwand,
Sichtbarkeit sichtbarkeitXp,
Sichtbarkeit sichtbarkeitLockhistorie,
Sichtbarkeit sichtbarkeitVorlieben,
Boolean profilBeiVeroeffentlichungenSichtbar) {}
@PutMapping("/me/picture")
public ResponseEntity<Void> updateProfilePicture(@RequestBody ProfilePictureRequest request, Principal principal) {
var user = userService.requireUser(principal);
user.setProfilePicture(request.picture());
user.setProfilePictureHq(request.pictureHq());
userRepository.save(user);
LOGGER.debug("User {} hat Profilbild aktualisiert", user.getUserId());
return ResponseEntity.ok().build();
}
@PutMapping("/me/profile")
public ResponseEntity<Void> updateProfile(@RequestBody ProfileRequest request, Principal principal) {
var user = userService.requireUser(principal);
if (request.beschreibung() != null && request.beschreibung().length() > 600) {
return ResponseEntity.badRequest().build();
}
user.setGroesse(request.groesse());
user.setGewicht(request.gewicht());
user.setGeschlecht(request.geschlecht());
user.setNeigung(request.neigung());
user.setBeziehungsstatus(request.beziehungsstatus());
user.setBeschreibung(request.beschreibung());
userRepository.save(user);
LOGGER.info("User {} hat Profil aktualisiert", user.getUserId());
return ResponseEntity.ok().build();
}
@PutMapping("/me/privacy")
public ResponseEntity<Void> updatePrivacy(@RequestBody PrivacyRequest request, Principal principal) {
var user = userService.requireUser(principal);
if (request.sichtbarkeitGrunddaten() != null) user.setSichtbarkeitGrunddaten(request.sichtbarkeitGrunddaten());
if (request.sichtbarkeitGalerie() != null) user.setSichtbarkeitGalerie(request.sichtbarkeitGalerie());
if (request.sichtbarkeitFreunde() != null) user.setSichtbarkeitFreunde(request.sichtbarkeitFreunde());
if (request.sichtbarkeitFeed() != null) user.setSichtbarkeitFeed(request.sichtbarkeitFeed());
if (request.sichtbarkeitPinnwand() != null) user.setSichtbarkeitPinnwand(request.sichtbarkeitPinnwand());
if (request.sichtbarkeitXp() != null) user.setSichtbarkeitXp(request.sichtbarkeitXp());
if (request.sichtbarkeitLockhistorie()!= null) user.setSichtbarkeitLockhistorie(request.sichtbarkeitLockhistorie());
if (request.sichtbarkeitVorlieben() != null) user.setSichtbarkeitVorlieben(request.sichtbarkeitVorlieben());
if (request.profilBeiVeroeffentlichungenSichtbar() != null) {
boolean showAuthor = request.profilBeiVeroeffentlichungenSichtbar();
user.setProfilBeiVeroeffentlichungenSichtbar(showAuthor);
// Alle veröffentlichten Templates synchronisieren
var templates = baseLockTemplateRepository.findByOwnerAndPublishedTrue(user.getUserId());
for (var t : templates) {
t.setShowAuthor(showAuthor);
}
baseLockTemplateRepository.saveAll(templates);
}
userRepository.save(user);
LOGGER.info("User {} hat Datenschutz-Einstellungen aktualisiert", user.getUserId());
return ResponseEntity.ok().build();
}
record NotificationPreferenceRequest(boolean inApp, boolean email) {}
@GetMapping("/me/notifications")
public ResponseEntity<Map<String, Object>> getNotifications(Principal principal) {
UUID userId = userService.requireUser(principal).getUserId();
Map<String, NotificationPreferenceEntity> byKey = notificationPreferenceRepository.findByUserId(userId)
.stream().collect(Collectors.toMap(p -> p.getCause().name(), p -> p));
Map<String, Object> result = new LinkedHashMap<>();
for (MessageCause cause : MessageCause.values()) {
NotificationPreferenceEntity pref = byKey.getOrDefault(
cause.name(), NotificationPreferenceEntity.defaultFor(userId, cause));
Map<String, Object> entry = new LinkedHashMap<>();
entry.put("inApp", pref.isInApp());
entry.put("email", pref.isEmail());
result.put(cause.name(), entry);
}
return ResponseEntity.ok(result);
}
@PutMapping("/me/notifications")
public ResponseEntity<Void> updateNotifications(@RequestBody Map<String, NotificationPreferenceRequest> request, Principal principal) {
UUID userId = userService.requireUser(principal).getUserId();
for (var entry : request.entrySet()) {
MessageCause cause;
try {
cause = MessageCause.valueOf(entry.getKey());
} catch (IllegalArgumentException e) {
continue;
}
NotificationPreferenceEntity pref = notificationPreferenceRepository
.findByUserIdAndCause(userId, cause)
.orElseGet(() -> {
NotificationPreferenceEntity n = new NotificationPreferenceEntity();
n.setUserId(userId);
n.setCause(cause);
return n;
});
pref.setInApp(entry.getValue().inApp());
pref.setEmail(entry.getValue().email());
notificationPreferenceRepository.save(pref);
}
return ResponseEntity.ok().build();
}
record BdsmDefaultsRequest(List<String> spieltMit, List<String> rollen, List<String> werkzeuge) {}
@GetMapping("/me/bdsm-defaults")
public ResponseEntity<Map<String, Object>> getBdsmDefaults(Principal principal) {
var currentUser = userService.requireUser(principal);
UUID userId = currentUser.getUserId();
BdsmDefaultsEntity d = bdsmDefaultsRepository.findByUserId(userId)
.orElse(new BdsmDefaultsEntity());
Map<String, Object> result = new java.util.LinkedHashMap<>();
result.put("geschlecht", currentUser.getGeschlecht() != null ? currentUser.getGeschlecht().name() : null);
result.put("spieltMit", splitOrEmpty(d.getSpieltMit()));
result.put("rollen", splitOrEmpty(d.getRollen()));
result.put("werkzeuge", splitOrEmpty(d.getWerkzeuge()));
return ResponseEntity.ok(result);
}
@GetMapping("/{userId}/bdsm-defaults")
public ResponseEntity<Map<String, Object>> getBdsmDefaultsForUser(@PathVariable UUID userId) {
var userOpt = userRepository.findById(userId);
if (userOpt.isEmpty()) return ResponseEntity.notFound().build();
UserEntity user = userOpt.get();
BdsmDefaultsEntity d = bdsmDefaultsRepository.findByUserId(userId)
.orElse(new BdsmDefaultsEntity());
Map<String, Object> result = new java.util.LinkedHashMap<>();
result.put("geschlecht", user.getGeschlecht() != null ? user.getGeschlecht().name() : null);
result.put("spieltMit", splitOrEmpty(d.getSpieltMit()));
result.put("rollen", splitOrEmpty(d.getRollen()));
result.put("werkzeuge", splitOrEmpty(d.getWerkzeuge()));
return ResponseEntity.ok(result);
}
@PutMapping("/me/bdsm-defaults")
public ResponseEntity<Void> updateBdsmDefaults(@RequestBody BdsmDefaultsRequest request, Principal principal) {
UUID userId = userService.requireUser(principal).getUserId();
BdsmDefaultsEntity d = bdsmDefaultsRepository.findByUserId(userId)
.orElseGet(() -> { BdsmDefaultsEntity n = new BdsmDefaultsEntity(); n.setUserId(userId); return n; });
d.setSpieltMit(request.spieltMit() == null ? "" : String.join(",", request.spieltMit()));
d.setRollen(request.rollen() == null ? "" : String.join(",", request.rollen()));
d.setWerkzeuge(request.werkzeuge() == null ? "" : String.join(",", request.werkzeuge()));
bdsmDefaultsRepository.save(d);
return ResponseEntity.ok().build();
}
private static List<String> splitOrEmpty(String s) {
if (s == null || s.isBlank()) return List.of();
return List.of(s.split(","));
}
@PutMapping("/me/geburtsdatum")
public ResponseEntity<Void> updateGeburtsdatum(@RequestBody GeburtsdatumChangeRequest request, Principal principal) {
if (request.geburtsdatum() == null
|| Period.between(request.geburtsdatum(), LocalDate.now()).getYears() < 18) {
return ResponseEntity.status(422).build();
}
var user = userService.requireUser(principal);
user.setGeburtsdatum(request.geburtsdatum());
userRepository.save(user);
LOGGER.info("User {} hat Geburtsdatum aktualisiert", user.getUserId());
return ResponseEntity.ok().build();
}
@PutMapping("/me/name")
public ResponseEntity<Void> updateName(@RequestBody NameChangeRequest request, Principal principal) {
String newName = request.name();
if (userRepository.findByName(newName).isPresent()
|| registrationRepository.findByName(newName).isPresent()) {
return ResponseEntity.status(409).build();
}
var user = userService.requireUser(principal);
user.setName(newName);
userRepository.save(user);
LOGGER.info("User {} hat Namen zu '{}' geändert", user.getUserId(), newName);
return ResponseEntity.ok().build();
}
@DeleteMapping("/me")
public ResponseEntity<Void> deleteAccount(Principal principal) {
var currentUser = userService.requireUser(principal);
UUID userId = currentUser.getUserId();
String email = currentUser.getEmail();
userService.deleteAccount(userId, email);
ResponseCookie cookie = ResponseCookie.from("jwt", "")
.httpOnly(true)
.sameSite("Strict")
.path("/")
.maxAge(0)
.build();
return ResponseEntity.ok()
.header(HttpHeaders.SET_COOKIE, cookie.toString())
.build();
}
// ── TTLock-Account ────────────────────────────────────────────────────────
@GetMapping("/me/ttlock")
public ResponseEntity<TtlockUserConfigDto> getTtlockUserConfig(Principal principal) {
UUID userId = userService.requireUser(principal).getUserId();
TTLockUserConfigEntity cfg = ttLockUserConfigRepository.findById(userId)
.orElse(new TTLockUserConfigEntity());
return ResponseEntity.ok(new TtlockUserConfigDto(
cfg.getUsername(),
cfg.getPasswordMd5() != null && !cfg.getPasswordMd5().isBlank(),
cfg.getLockId(),
cfg.isTestSuccessful()
));
}
@PutMapping("/me/ttlock")
public ResponseEntity<Void> saveTtlockUserConfig(@RequestBody TtlockUserConfigRequest body, Principal principal) {
UUID userId = userService.requireUser(principal).getUserId();
TTLockUserConfigEntity cfg = ttLockUserConfigRepository.findById(userId)
.orElseGet(() -> { TTLockUserConfigEntity n = new TTLockUserConfigEntity(); n.setUserId(userId); return n; });
boolean credentialsChanged = !java.util.Objects.equals(cfg.getUsername(), body.username())
|| !java.util.Objects.equals(cfg.getLockId(), body.lockId())
|| (body.password() != null && !body.password().isBlank());
if (credentialsChanged) {
cfg.setTestSuccessful(false);
}
cfg.setUsername(body.username());
if (body.password() != null && !body.password().isBlank()) {
cfg.setPasswordMd5(DigestUtils.md5DigestAsHex(body.password().getBytes(StandardCharsets.UTF_8)));
}
cfg.setLockId(body.lockId());
ttLockUserConfigRepository.save(cfg);
return ResponseEntity.ok().build();
}
@GetMapping("/me/ttlock/test")
public ResponseEntity<Map<String, Object>> testTtlockConnection(Principal principal) {
UUID userId = userService.requireUser(principal).getUserId();
var userCfg = ttLockUserConfigRepository.findById(userId).orElse(null);
if (userCfg == null || userCfg.getUsername() == null || userCfg.getPasswordMd5() == null || userCfg.getLockId() == null) {
return ResponseEntity.badRequest().body(Map.of("error", "ttlock_not_configured"));
}
var adminCfg = ttLockConfigRepository.findById(1L).orElse(null);
if (adminCfg == null || adminCfg.getClientId() == null || adminCfg.getClientSecret() == null) {
return ResponseEntity.badRequest().body(Map.of("error", "admin_config_missing"));
}
String token = ttAuthService.getAccessToken(
adminCfg.getClientId(), adminCfg.getClientSecret(),
userCfg.getUsername(), userCfg.getPasswordMd5());
if (token == null) {
return ResponseEntity.status(502).body(Map.of("error", "auth_failed"));
}
TTLockService.TTLockDetailResponse detail = ttLockService.getLockDetail(
adminCfg.getClientId(), token, userCfg.getLockId());
if (detail == null || detail.getErrcode() != 0) {
String msg = detail != null ? detail.getErrmsg() : "Keine Antwort";
return ResponseEntity.status(502).body(Map.of("error", "lock_detail_failed", "message", msg));
}
userCfg.setTestSuccessful(true);
ttLockUserConfigRepository.save(userCfg);
Map<String, Object> result = new LinkedHashMap<>();
result.put("lockId", detail.getLockId());
result.put("lockName", detail.getLockName());
result.put("lockAlias", detail.getLockAlias());
result.put("modelNum", detail.getModelNum());
result.put("electricQuantity", detail.getElectricQuantity());
result.put("state", detail.getState());
return ResponseEntity.ok(result);
}
@PostMapping("/me/ttlock/open")
public ResponseEntity<Map<String, Object>> ttlockOpen(Principal principal) {
UUID userId = userService.requireUser(principal).getUserId();
var userCfg = ttLockUserConfigRepository.findById(userId).orElse(null);
if (userCfg == null || userCfg.getUsername() == null || userCfg.getPasswordMd5() == null || userCfg.getLockId() == null) {
return ResponseEntity.badRequest().body(Map.of("error", "ttlock_not_configured"));
}
var adminCfg = ttLockConfigRepository.findById(1L).orElse(null);
if (adminCfg == null || adminCfg.getClientId() == null) {
return ResponseEntity.badRequest().body(Map.of("error", "admin_config_missing"));
}
var activeLock = baseLockRepository.findByLockee(userId);
if (activeLock.isPresent() && activeLock.get().getUnlockTime() == null) {
return ResponseEntity.status(409).body(Map.of("error", "active_lock_exists"));
}
String token = ttAuthService.getAccessToken(
adminCfg.getClientId(), adminCfg.getClientSecret(),
userCfg.getUsername(), userCfg.getPasswordMd5());
if (token == null) {
return ResponseEntity.status(502).body(Map.of("error", "auth_failed"));
}
String pin = CodeCreator.createNumeric(6);
Integer pwdId = ttLockService.addCustomPasscode(adminCfg.getClientId(), token, userCfg.getLockId(), pin);
if (pwdId == null) {
return ResponseEntity.status(502).body(Map.of("error", "passcode_failed"));
}
return ResponseEntity.ok(Map.of("pin", pin, "keyboardPwdId", pwdId));
}
@DeleteMapping("/me/ttlock/open/{keyboardPwdId}")
public ResponseEntity<Void> ttlockCloseOpen(@PathVariable int keyboardPwdId, Principal principal) {
UUID userId = userService.requireUser(principal).getUserId();
var userCfg = ttLockUserConfigRepository.findById(userId).orElse(null);
if (userCfg == null || userCfg.getLockId() == null) return ResponseEntity.badRequest().build();
var adminCfg = ttLockConfigRepository.findById(1L).orElse(null);
if (adminCfg == null) return ResponseEntity.badRequest().build();
String token = ttAuthService.getAccessToken(
adminCfg.getClientId(), adminCfg.getClientSecret(),
userCfg.getUsername(), userCfg.getPasswordMd5());
if (token == null) return ResponseEntity.status(502).build();
ttLockService.deleteCustomPasscode(adminCfg.getClientId(), token, userCfg.getLockId(), keyboardPwdId);
return ResponseEntity.ok().build();
}
@PostMapping
public ResponseEntity<Void> userAnlegen(@RequestBody Registration registration) {
try {
userService.createUser(registration);
return ResponseEntity.status(201).build();
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
} catch (IllegalStateException e) {
return ResponseEntity.status(409).build();
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
return ResponseEntity.internalServerError().build();
}
}
}