added basic eg managment
This commit is contained in:
parent
ad47f7399e
commit
81ee804ae1
@ -1,38 +0,0 @@
|
||||
2026-06-16 18:27:44.502501+02:00 database: close
|
||||
org.h2.message.DbException: Eingabe/Ausgabe Fehler: "Closing"
|
||||
IO Exception: "Closing" [90028-240]
|
||||
at org.h2.message.DbException.get(DbException.java:212)
|
||||
at org.h2.mvstore.db.Store.close(Store.java:352)
|
||||
at org.h2.engine.Database.closeOpenFilesAndUnlock(Database.java:1314)
|
||||
at org.h2.engine.Database.closeImpl(Database.java:1276)
|
||||
at org.h2.engine.Database.close(Database.java:1195)
|
||||
at org.h2.engine.Database.onShutdown(Database.java:1180)
|
||||
at org.h2.engine.OnExitDatabaseCloser.onShutdown(OnExitDatabaseCloser.java:85)
|
||||
at org.h2.engine.OnExitDatabaseCloser.run(OnExitDatabaseCloser.java:114)
|
||||
Caused by: org.h2.jdbc.JdbcSQLNonTransientException: Eingabe/Ausgabe Fehler: "Closing"
|
||||
IO Exception: "Closing" [90028-240]
|
||||
at org.h2.message.DbException.getJdbcSQLException(DbException.java:566)
|
||||
at org.h2.message.DbException.getJdbcSQLException(DbException.java:489)
|
||||
... 8 more
|
||||
Caused by: org.h2.mvstore.MVStoreException: java.lang.AssertionError: 8 [2.4.240/3]
|
||||
at org.h2.mvstore.DataUtils.newMVStoreException(DataUtils.java:996)
|
||||
at org.h2.mvstore.MVStore.executeFilestoreOperation(MVStore.java:1048)
|
||||
at org.h2.mvstore.RandomAccessStore.compactMoveChunks(RandomAccessStore.java:463)
|
||||
at org.h2.mvstore.RandomAccessStore.compactStore(RandomAccessStore.java:443)
|
||||
at org.h2.mvstore.FileStore.compactStore(FileStore.java:891)
|
||||
at org.h2.mvstore.FileStore.stop(FileStore.java:275)
|
||||
at org.h2.mvstore.MVStore.closeStore(MVStore.java:803)
|
||||
at org.h2.mvstore.MVStore.close(MVStore.java:763)
|
||||
at org.h2.mvstore.db.Store.close(Store.java:348)
|
||||
... 6 more
|
||||
Caused by: java.lang.AssertionError: 8
|
||||
at org.h2.mvstore.FreeSpaceBitSet.allocate(FreeSpaceBitSet.java:153)
|
||||
at org.h2.mvstore.FreeSpaceBitSet.predictAllocation(FreeSpaceBitSet.java:133)
|
||||
at org.h2.mvstore.RandomAccessStore.predictAllocation(RandomAccessStore.java:101)
|
||||
at org.h2.mvstore.RandomAccessStore.moveChunkInside(RandomAccessStore.java:628)
|
||||
at org.h2.mvstore.RandomAccessStore.compactMoveChunks(RandomAccessStore.java:569)
|
||||
at org.h2.mvstore.RandomAccessStore.compactMoveChunks(RandomAccessStore.java:481)
|
||||
at org.h2.mvstore.RandomAccessStore.lambda$compactMoveChunks$1(RandomAccessStore.java:468)
|
||||
at org.h2.mvstore.FileStore.executeFileStoreOperation(FileStore.java:1894)
|
||||
at org.h2.mvstore.MVStore.executeFilestoreOperation(MVStore.java:1044)
|
||||
... 13 more
|
||||
@ -1,9 +0,0 @@
|
||||
package at.mueller.eeg.backend.common.event;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public record MeteringPointApprovedEvent(
|
||||
UUID userId,
|
||||
String meteringPointNumber,
|
||||
String type // "BEZUG" oder "EINSPEISUNG"
|
||||
) {}
|
||||
@ -0,0 +1,68 @@
|
||||
package at.mueller.eeg.backend.community.api;
|
||||
|
||||
import at.mueller.eeg.backend.community.domain.EnergyCommunity;
|
||||
import at.mueller.eeg.backend.community.service.EnergyCommunityService;
|
||||
import io.swagger.v3.oas.annotations.media.ArraySchema;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/admin/energy-communities")
|
||||
@RequiredArgsConstructor
|
||||
public class EnergyCommunityAdminController {
|
||||
|
||||
private final EnergyCommunityService service;
|
||||
|
||||
// GET /api/admin/energy-communities
|
||||
@GetMapping
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "Liste der wartenden Benutzer erfolgreich geladen",
|
||||
content = @Content(
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE,
|
||||
array = @ArraySchema(
|
||||
schema = @Schema(implementation = EnergyCommunity.class)
|
||||
)
|
||||
)
|
||||
)
|
||||
public ResponseEntity<List<EnergyCommunity>> getAllEnergyCommunities() {
|
||||
return ResponseEntity.ok(service.findAll());
|
||||
}
|
||||
|
||||
// GET /api/admin/energy-communities/{id}
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<EnergyCommunity> getEnergyCommunityById(@PathVariable UUID id) {
|
||||
return ResponseEntity.ok(service.findById(id));
|
||||
}
|
||||
|
||||
// POST /api/admin/energy-communities
|
||||
@PostMapping
|
||||
public ResponseEntity<EnergyCommunity> createEnergyCommunity(@RequestBody EnergyCommunity community) {
|
||||
EnergyCommunity createdCommunity = service.create(community);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(createdCommunity);
|
||||
}
|
||||
|
||||
// PUT /api/admin/energy-communities/{id}
|
||||
@PutMapping("/{id}")
|
||||
public ResponseEntity<EnergyCommunity> updateEnergyCommunity(
|
||||
@PathVariable UUID id,
|
||||
@RequestBody EnergyCommunity community) {
|
||||
return ResponseEntity.ok(service.update(id, community));
|
||||
}
|
||||
|
||||
// DELETE /api/admin/energy-communities/{id}
|
||||
@DeleteMapping("/{id}")
|
||||
public ResponseEntity<Void> deleteEnergyCommunity(@PathVariable UUID id) {
|
||||
service.delete(id);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,7 @@ import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@ -28,6 +29,9 @@ public class MeteringPoint {
|
||||
@Enumerated(EnumType.STRING)
|
||||
private PointType type;
|
||||
|
||||
@Column(name = "consent_requested_at")
|
||||
private LocalDateTime consentRequestedAt;
|
||||
|
||||
@Column(name = "grid_operator_id", length = 50)
|
||||
private String gridOperatorId;
|
||||
|
||||
@ -50,8 +54,17 @@ public class MeteringPoint {
|
||||
public void fireTrigger(MakoTrigger trigger) {
|
||||
this.makoState = switch (this.makoState) {
|
||||
|
||||
case NEW ->
|
||||
trigger == MakoTrigger.SEND_REQUEST ? MakoState.WAITING_FOR_CONSENT : invalid(trigger);
|
||||
case NEW, ERROR -> switch (trigger) {
|
||||
case SEND_REQUEST -> {
|
||||
this.consentRequestedAt = LocalDateTime.now();
|
||||
yield MakoState.WAITING_FOR_CONSENT;
|
||||
}
|
||||
case TECHNICAL_FAILURE -> {
|
||||
this.consentRequestedAt = null;
|
||||
yield MakoState.ERROR;
|
||||
}
|
||||
default -> invalid(trigger);
|
||||
};
|
||||
|
||||
case WAITING_FOR_CONSENT -> switch (trigger) {
|
||||
case CONSENT_APPROVED -> MakoState.CONSENT_GRANTED;
|
||||
@ -67,10 +80,6 @@ public class MeteringPoint {
|
||||
default -> invalid(trigger);
|
||||
};
|
||||
|
||||
// Aus einem Fehlerzustand heraus kann man den Prozess z.B. neu starten
|
||||
case ERROR ->
|
||||
trigger == MakoTrigger.SEND_REQUEST ? MakoState.WAITING_FOR_CONSENT : invalid(trigger);
|
||||
|
||||
// Endzustände (ACTIVE, REJECTED) erlauben standardmäßig keine weiteren Trigger
|
||||
default -> invalid(trigger);
|
||||
};
|
||||
|
||||
@ -6,6 +6,7 @@ import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface EnergyCommunityRepository extends JpaRepository<EnergyCommunity, UUID> {
|
||||
@ -21,4 +22,6 @@ public interface EnergyCommunityRepository extends JpaRepository<EnergyCommunity
|
||||
@Param("transformerId") String transformerId,
|
||||
@Param("gridOperatorId") String gridOperatorId
|
||||
);
|
||||
|
||||
Optional<EnergyCommunity> findByEdaEcId(String edaEcId);
|
||||
}
|
||||
|
||||
@ -0,0 +1,53 @@
|
||||
package at.mueller.eeg.backend.community.service;
|
||||
|
||||
import at.mueller.eeg.backend.community.domain.EnergyCommunity;
|
||||
import at.mueller.eeg.backend.community.repository.EnergyCommunityRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class EnergyCommunityService {
|
||||
|
||||
private final EnergyCommunityRepository repository;
|
||||
|
||||
public List<EnergyCommunity> findAll() {
|
||||
return repository.findAll();
|
||||
}
|
||||
|
||||
public EnergyCommunity findById(UUID id) {
|
||||
return repository.findById(id)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Energy Community mit ID " + id + " nicht gefunden."));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public EnergyCommunity create(EnergyCommunity community) {
|
||||
return repository.save(community);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public EnergyCommunity update(UUID id, EnergyCommunity updatedData) {
|
||||
EnergyCommunity existing = findById(id);
|
||||
|
||||
existing.setEdaEcId(updatedData.getEdaEcId());
|
||||
existing.setType(updatedData.getType());
|
||||
existing.setName(updatedData.getName());
|
||||
existing.setGridOperatorId(updatedData.getGridOperatorId());
|
||||
existing.setSubstationId(updatedData.getSubstationId());
|
||||
existing.setTransformerId(updatedData.getTransformerId());
|
||||
|
||||
return repository.save(existing);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void delete(UUID id) {
|
||||
if (!repository.existsById(id)) {
|
||||
throw new IllegalArgumentException("Energy Community mit ID " + id + " existiert nicht.");
|
||||
}
|
||||
repository.deleteById(id);
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,9 @@
|
||||
package at.mueller.eeg.backend.iam.api;
|
||||
|
||||
import at.mueller.eeg.backend.common.event.UserApprovedEvent;
|
||||
import at.mueller.eeg.backend.iam.api.dto.IamDtos.UserProfileResponse;
|
||||
import at.mueller.eeg.backend.iam.domain.RegistrationStatus;
|
||||
import at.mueller.eeg.backend.iam.domain.User;
|
||||
import at.mueller.eeg.backend.iam.mapper.UserMapper;
|
||||
import at.mueller.eeg.backend.iam.repository.UserRepository;
|
||||
import at.mueller.eeg.backend.iam.service.AdminIamService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.ArraySchema;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
@ -13,7 +11,6 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@ -31,12 +28,9 @@ import java.util.UUID;
|
||||
@RequiredArgsConstructor
|
||||
public class AdminIamController {
|
||||
|
||||
private final UserRepository userRepository;
|
||||
|
||||
private final AdminIamService adminIamService;
|
||||
private final UserMapper userMapper;
|
||||
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
|
||||
@GetMapping("/pending")
|
||||
@Operation(summary = "Lädt alle wartenden User",
|
||||
description = "Zeigt User im Status PENDING inkl. der Ergebnisse des EDA-Netz-Checks.")
|
||||
@ -51,7 +45,7 @@ public class AdminIamController {
|
||||
)
|
||||
)
|
||||
public ResponseEntity<List<UserProfileResponse>> getPendingUsers() {
|
||||
List<User> allByStatus = userRepository.findAllByStatus(RegistrationStatus.PENDING);
|
||||
List<User> allByStatus = adminIamService.getPendingUsers();
|
||||
return ResponseEntity.ok(userMapper.mapToDto(allByStatus));
|
||||
}
|
||||
|
||||
@ -59,23 +53,14 @@ public class AdminIamController {
|
||||
@Operation(summary = "User freigeben",
|
||||
description = "Setzt enabled=true, status=APPROVED und triggert die finale EDA-Anmeldung.")
|
||||
public ResponseEntity<Void> approveUser(@PathVariable UUID userId) {
|
||||
User user = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found"));
|
||||
|
||||
if (!RegistrationStatus.PENDING.equals(user.getStatus())) {
|
||||
throw new ResponseStatusException(HttpStatus.CONFLICT, "User is not in PENDING status");
|
||||
}
|
||||
if (user.isEnabled()) {
|
||||
throw new ResponseStatusException(HttpStatus.CONFLICT, "User is already enabled");
|
||||
}
|
||||
|
||||
user.setStatus(RegistrationStatus.APPROVED);
|
||||
user.setEnabled(true);
|
||||
userRepository.save(user);
|
||||
|
||||
eventPublisher.publishEvent(new UserApprovedEvent(user.getId()));
|
||||
|
||||
try {
|
||||
adminIamService.approveUser(userId);
|
||||
return ResponseEntity.noContent().build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND, e.getMessage());
|
||||
} catch (IllegalStateException e) {
|
||||
throw new ResponseStatusException(HttpStatus.CONFLICT, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/{userId}/reject")
|
||||
@ -85,6 +70,7 @@ public class AdminIamController {
|
||||
// Logik: status auf REJECTED setzen oder User hard-löschen, je nach Datenschutz-Konzept
|
||||
// Event feuern: UserRejectedEvent
|
||||
|
||||
adminIamService.rejectUser(userId);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
package at.mueller.eeg.backend.iam.service;
|
||||
|
||||
import at.mueller.eeg.backend.common.event.UserApprovedEvent;
|
||||
import at.mueller.eeg.backend.iam.domain.RegistrationStatus;
|
||||
import at.mueller.eeg.backend.iam.domain.User;
|
||||
import at.mueller.eeg.backend.iam.repository.UserRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AdminIamService {
|
||||
|
||||
private final UserRepository userRepository;
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
|
||||
public List<User> getPendingUsers() {
|
||||
return userRepository.findAllByStatus(RegistrationStatus.PENDING);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void approveUser(UUID userId) {
|
||||
User user = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("User nicht gefunden"));
|
||||
|
||||
if (!RegistrationStatus.PENDING.equals(user.getStatus())) {
|
||||
throw new IllegalStateException("User ist nicht im Status PENDING");
|
||||
}
|
||||
if (user.isEnabled()) {
|
||||
throw new IllegalStateException("User ist bereits aktiviert");
|
||||
}
|
||||
|
||||
user.setStatus(RegistrationStatus.APPROVED);
|
||||
user.setEnabled(true);
|
||||
userRepository.save(user);
|
||||
|
||||
eventPublisher.publishEvent(new UserApprovedEvent(user.getId()));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void rejectUser(UUID userId) {
|
||||
// Hier folgt später die Logik für das Ablehnen
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,8 @@
|
||||
package at.mueller.eeg.backend.mako.event.listener;
|
||||
|
||||
import at.mueller.eeg.backend.common.event.EdaTopologyCheckEvent;
|
||||
import at.mueller.eeg.backend.common.event.EdaTopologyCompletedEvent;
|
||||
import at.mueller.eeg.backend.common.event.EdaTopologyFailedEvent;
|
||||
import at.mueller.eeg.backend.common.event.*;
|
||||
import at.mueller.eeg.backend.mako.api.dto.EdaTopologyResponse;
|
||||
import at.mueller.eeg.backend.mako.service.EdaConsentService;
|
||||
import at.mueller.eeg.backend.mako.service.EdaTopologyService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -19,6 +18,7 @@ import org.springframework.stereotype.Component;
|
||||
public class EdaCommuncationEventListener {
|
||||
|
||||
private final EdaTopologyService edaTopologyService;
|
||||
private final EdaConsentService edaConsentService;
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
|
||||
@EventListener
|
||||
@ -51,4 +51,30 @@ public class EdaCommuncationEventListener {
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@EventListener
|
||||
@Async
|
||||
public void handleInitiateConsentEvent(InitiateMakoConsentEvent event) {
|
||||
log.info("MaKo-Domain: Asynchroner Consent-Request gestartet für AT-Nummer: {} (Typ: {})",
|
||||
event.atNumber(), event.pointType());
|
||||
|
||||
try {
|
||||
edaConsentService.sendConsentRequest(event.atNumber(), event.pointType());
|
||||
|
||||
log.info("MaKo-Domain: Consent-Request erfolgreich an EDA übermittelt für {}", event.atNumber());
|
||||
|
||||
// Da der Netzbetreiber jetzt asynchron antwortet (entweder via Webhook oder Mailbox),
|
||||
// endet der MaKo-Job hier vorerst. Die Community-Domain wartet im Status WAITING_FOR_CONSENT.
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("MaKo-Domain: Fehler beim Senden des Consent-Requests für AT-Nummer {}: {}",
|
||||
event.atNumber(), e.getMessage());
|
||||
|
||||
eventPublisher.publishEvent(new MakoConsensTechnicalFailedEvent(
|
||||
event.atNumber(),
|
||||
e.getMessage()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
package at.mueller.eeg.backend.mako.event.listener;
|
||||
|
||||
public class MakoCommandListener {
|
||||
}
|
||||
@ -41,6 +41,63 @@ paths:
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
/api/admin/energy-communities/{id}:
|
||||
get:
|
||||
tags:
|
||||
- energy-community-admin-controller
|
||||
operationId: getEnergyCommunityById
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
'*/*':
|
||||
schema:
|
||||
$ref: "#/components/schemas/EnergyCommunity"
|
||||
put:
|
||||
tags:
|
||||
- energy-community-admin-controller
|
||||
operationId: updateEnergyCommunity
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EnergyCommunity"
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
'*/*':
|
||||
schema:
|
||||
$ref: "#/components/schemas/EnergyCommunity"
|
||||
delete:
|
||||
tags:
|
||||
- energy-community-admin-controller
|
||||
operationId: deleteEnergyCommunity
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
/api/community/memberships/request:
|
||||
post:
|
||||
tags:
|
||||
@ -132,6 +189,37 @@ paths:
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
/api/admin/energy-communities:
|
||||
get:
|
||||
tags:
|
||||
- energy-community-admin-controller
|
||||
operationId: getAllEnergyCommunities
|
||||
responses:
|
||||
"200":
|
||||
description: Liste der wartenden Benutzer erfolgreich geladen
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/EnergyCommunity"
|
||||
post:
|
||||
tags:
|
||||
- energy-community-admin-controller
|
||||
operationId: createEnergyCommunity
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EnergyCommunity"
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
'*/*':
|
||||
schema:
|
||||
$ref: "#/components/schemas/EnergyCommunity"
|
||||
/api/community/metering-points/{id}/eligible-communities:
|
||||
get:
|
||||
tags:
|
||||
@ -171,6 +259,28 @@ paths:
|
||||
$ref: "#/components/schemas/UserProfileResponse"
|
||||
components:
|
||||
schemas:
|
||||
EnergyCommunity:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
edaEcId:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- EEG_LOKAL
|
||||
- EEG_REGIONAL
|
||||
- BEG
|
||||
name:
|
||||
type: string
|
||||
gridOperatorId:
|
||||
type: string
|
||||
substationId:
|
||||
type: string
|
||||
transformerId:
|
||||
type: string
|
||||
MembershipRequest:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
<!-- Der Header bleibt immer fix oben stehen -->
|
||||
<app-header></app-header>
|
||||
|
||||
@if (!isAdminRoute) {
|
||||
<app-header></app-header>
|
||||
}
|
||||
<!-- Hier drin werden je nach Route die Login-, Register- oder Dashboard-Seiten geladen -->
|
||||
<main class="min-h-[calc(100vh-4rem)] bg-gray-50">
|
||||
<router-outlet></router-outlet>
|
||||
|
||||
@ -4,14 +4,34 @@ import {Register} from './pages/register/register';
|
||||
import {LoginComponent} from './pages/login/login';
|
||||
import {AdminUserApprovalComponent} from './pages/admin-user-approval/admin-user-approval';
|
||||
import {adminAuthGuard} from './guards/admin-auth-guard';
|
||||
import {AppLayout} from './layout/app-layout/app-layout';
|
||||
import {AdminEnergyCommunity} from './pages/admin-energy-community/admin-energy-community';
|
||||
|
||||
export const routes: Routes = [
|
||||
{ path: '', component: LandingPage },
|
||||
{ path: 'register', component: Register },
|
||||
{ path: 'login', component: LoginComponent},
|
||||
{
|
||||
path: 'admin/approvals',
|
||||
component: AdminUserApprovalComponent,
|
||||
canActivate: [adminAuthGuard]
|
||||
path: 'admin',
|
||||
component: AppLayout,
|
||||
canActivate: [adminAuthGuard],
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'approvals',
|
||||
pathMatch: 'full'
|
||||
},
|
||||
{
|
||||
path: 'approvals',
|
||||
component: AdminUserApprovalComponent
|
||||
},
|
||||
{
|
||||
path: 'energy-communities',
|
||||
component: AdminEnergyCommunity
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// Wildcard-Fallback für unbekannte URLs
|
||||
{ path: '**', redirectTo: '' }
|
||||
];
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import {Component, signal} from '@angular/core';
|
||||
import {RouterOutlet} from '@angular/router';
|
||||
import {NavigationEnd, Router, RouterOutlet} from '@angular/router';
|
||||
import {HeaderComponent} from './layout/header/header';
|
||||
|
||||
@Component({
|
||||
@ -10,4 +10,14 @@ import {HeaderComponent} from './layout/header/header';
|
||||
})
|
||||
export class App {
|
||||
protected readonly title = signal('eeg_frontend');
|
||||
|
||||
isAdminRoute = false;
|
||||
|
||||
constructor(private router: Router) {
|
||||
this.router.events.subscribe(event => {
|
||||
if (event instanceof NavigationEnd) {
|
||||
this.isAdminRoute = event.urlAfterRedirects.startsWith('/admin');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import {inject} from '@angular/core';
|
||||
import {CanActivateFn, Router} from '@angular/router';
|
||||
import {AuthService} from '../services/auth'; // Adjust path if necessary
|
||||
import {AuthService} from '../services/auth';
|
||||
|
||||
export const adminAuthGuard: CanActivateFn = (route, state) => {
|
||||
const authService = inject(AuthService);
|
||||
@ -8,7 +8,7 @@ export const adminAuthGuard: CanActivateFn = (route, state) => {
|
||||
|
||||
// Replace these with your actual authentication and role-checking methods
|
||||
const isAuthenticated = authService.isLoggedIn();
|
||||
const isAdmin = authService.getUserRole() === 'ADMIN';
|
||||
const isAdmin = authService.currentUserRole() === 'ADMIN';
|
||||
if (isAuthenticated && isAdmin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
47
eeg_frontend/src/app/layout/app-layout/app-layout.html
Normal file
47
eeg_frontend/src/app/layout/app-layout/app-layout.html
Normal file
@ -0,0 +1,47 @@
|
||||
<div class="flex h-screen bg-slate-50">
|
||||
|
||||
<aside class="w-64 bg-white border-r border-slate-200 flex flex-col z-10 shrink-0">
|
||||
<div class="h-16 flex items-center px-6 border-b border-slate-200 shrink-0">
|
||||
<a routerLink="/" class="flex items-center space-x-2 group">
|
||||
<svg class="h-7 w-7 text-emerald-600" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" />
|
||||
</svg>
|
||||
<span class="font-bold text-xl text-slate-900 tracking-tight">
|
||||
eeg<span class="text-emerald-600">portal</span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<nav class="flex-1 px-4 py-6 space-y-1 overflow-y-auto">
|
||||
<a routerLink="/admin/approvals" routerLinkActive="bg-emerald-50 text-emerald-700 font-medium"
|
||||
class="block px-4 py-2.5 rounded-lg text-slate-600 hover:bg-slate-50 hover:text-slate-900 transition-colors">
|
||||
Benutzerverwaltung
|
||||
</a>
|
||||
<a routerLink="/admin/energy-communities" routerLinkActive="bg-emerald-50 text-emerald-700 font-medium"
|
||||
class="block px-4 py-2.5 rounded-lg text-slate-600 hover:bg-slate-50 hover:text-slate-900 transition-colors">
|
||||
Energiegemeinschaften
|
||||
</a>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<main class="flex-1 flex flex-col min-w-0 overflow-hidden">
|
||||
|
||||
<header class="h-16 bg-white border-b border-slate-200 flex items-center px-8 justify-between shrink-0">
|
||||
<h1 class="text-lg font-semibold text-slate-800">Verwaltung</h1>
|
||||
|
||||
<div class="flex items-center gap-2 text-sm text-slate-600 bg-slate-50 px-3 py-1.5 rounded-full border border-slate-200">
|
||||
<span class="relative flex h-2.5 w-2.5">
|
||||
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>
|
||||
<span class="relative inline-flex rounded-full h-2.5 w-2.5 bg-emerald-500"></span>
|
||||
</span>
|
||||
Admin
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="flex-1 overflow-auto p-8">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
</div>
|
||||
22
eeg_frontend/src/app/layout/app-layout/app-layout.spec.ts
Normal file
22
eeg_frontend/src/app/layout/app-layout/app-layout.spec.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {AppLayout} from './app-layout';
|
||||
|
||||
describe('AdminLayout', () => {
|
||||
let component: AppLayout;
|
||||
let fixture: ComponentFixture<AppLayout>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [AppLayout],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(AppLayout);
|
||||
component = fixture.componentInstance;
|
||||
await fixture.whenStable();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
11
eeg_frontend/src/app/layout/app-layout/app-layout.ts
Normal file
11
eeg_frontend/src/app/layout/app-layout/app-layout.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {RouterLink, RouterLinkActive, RouterOutlet} from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-admin-layout',
|
||||
standalone: true,
|
||||
imports: [RouterOutlet, RouterLink, RouterLinkActive],
|
||||
templateUrl: './app-layout.html',
|
||||
styleUrl: './app-layout.scss'
|
||||
})
|
||||
export class AppLayout {}
|
||||
@ -25,7 +25,7 @@
|
||||
@if (authService.isLoggedIn$ | async) {
|
||||
|
||||
<span class="text-sm text-gray-500 hidden sm:inline">
|
||||
Rolle: <span class="font-semibold text-gray-700">{{ authService.getUserRole() }}</span>
|
||||
Rolle: <span class="font-semibold text-gray-700">{{ authService.currentUserRole() }}</span>
|
||||
</span>
|
||||
<button (click)="onLogout()" class="text-sm font-medium text-red-600 hover:text-red-700 border border-red-200 hover:border-red-300 px-3 py-1.5 rounded-md transition duration-150">
|
||||
Abmelden
|
||||
|
||||
@ -0,0 +1,142 @@
|
||||
<div class="bg-white rounded-2xl shadow-sm border border-slate-200 p-6">
|
||||
|
||||
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center mb-6 gap-4">
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold text-slate-800">Energiegemeinschaften</h2>
|
||||
<p class="text-sm text-slate-500 mt-1">Verwalte alle EGs und deren Netz-Zuordnungen.</p>
|
||||
</div>
|
||||
<button (click)="openCreateModal()" class="bg-blue-600 hover:bg-blue-700 text-white px-5 py-2.5 rounded-xl font-medium transition-colors shadow-sm">
|
||||
+ Neue EG anlegen
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto ring-1 ring-slate-200 rounded-xl">
|
||||
<table class="w-full text-left border-collapse">
|
||||
<thead>
|
||||
<tr class="bg-slate-50 text-sm text-slate-500 border-b border-slate-200">
|
||||
<th class="py-4 px-6 font-semibold">Name</th>
|
||||
<th class="py-4 px-6 font-semibold">EDA ID</th>
|
||||
<th class="py-4 px-6 font-semibold">Typ</th>
|
||||
<th class="py-4 px-6 font-semibold">Netzbetreiber</th>
|
||||
<th class="py-4 px-6 font-semibold text-right">Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-sm divide-y divide-slate-100">
|
||||
@for (ec of communities; track ec.id) {
|
||||
<tr class="hover:bg-slate-50/50 transition-colors">
|
||||
<td class="py-4 px-6 font-medium text-slate-900">{{ ec.name }}</td>
|
||||
<td class="py-4 px-6 text-slate-600 font-mono text-xs">{{ ec.edaEcId }}</td>
|
||||
<td class="py-4 px-6 text-slate-600">
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-50 text-blue-700">
|
||||
{{ ec.type }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="py-4 px-6 text-slate-600">{{ ec.gridOperatorId || '-' }}</td>
|
||||
<td class="py-4 px-6 text-right space-x-3">
|
||||
<button (click)="openEditModal(ec)" class="text-blue-600 hover:text-blue-800 font-medium transition-colors">
|
||||
Bearbeiten
|
||||
</button>
|
||||
<button (click)="onDelete(ec.id)"
|
||||
[disabled]="!canDelete(ec)"
|
||||
[class.opacity-40]="!canDelete(ec)"
|
||||
class="text-red-600 hover:text-red-800 disabled:hover:text-red-600 font-medium transition-colors">
|
||||
Löschen
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
} @empty {
|
||||
<tr>
|
||||
<td colspan="5" class="py-12 text-center text-slate-500">
|
||||
<div class="flex flex-col items-center justify-center">
|
||||
<span class="text-lg font-medium text-slate-600">Keine Daten gefunden</span>
|
||||
<span class="text-sm mt-1">Es wurden noch keine Energiegemeinschaften angelegt.</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@if (isModalOpen) {
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center bg-slate-900/50 backdrop-blur-sm transition-opacity">
|
||||
|
||||
<div class="bg-white rounded-2xl shadow-xl w-full max-w-lg overflow-hidden flex flex-col max-h-[90vh]">
|
||||
|
||||
<div class="px-6 py-4 border-b border-slate-200 flex justify-between items-center bg-slate-50 shrink-0">
|
||||
<h3 class="text-lg font-bold text-slate-800">Neue Energiegemeinschaft anlegen</h3>
|
||||
<button (click)="closeModal()" class="text-slate-400 hover:text-slate-600 transition-colors">
|
||||
<svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="p-6 overflow-y-auto">
|
||||
<form [formGroup]="createForm" (ngSubmit)="onSubmit()" id="createEcForm" class="space-y-4">
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700 mb-1">Name der Gemeinschaft</label>
|
||||
<input type="text" formControlName="name"
|
||||
class="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-all">
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700 mb-1">EDA ID</label>
|
||||
<input type="text" formControlName="edaEcId" placeholder="z.B. AT..."
|
||||
class="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-all">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700 mb-1">Typ</label>
|
||||
<select formControlName="type"
|
||||
class="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-all bg-white">
|
||||
<option value="" disabled selected>Bitte wählen</option>
|
||||
<option value="BEG">BEG (Bürgerenergiegemeinschaft)</option>
|
||||
<option value="EEG_REGIONAL">Lokale EEG (Lokale Erneuerbare-Energie-Gemeinschaft)</option>
|
||||
<option value="EEG_LOKAL">Lokale EEG (Lokale Erneuerbare-Energie-Gemeinschaft)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border-t border-slate-200 my-4 pt-4">
|
||||
<h4 class="text-sm font-semibold text-slate-800 mb-3">Netz-Topologie (Optional)</h4>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700 mb-1">Netzbetreiber ID</label>
|
||||
<input type="text" formControlName="gridOperatorId"
|
||||
class="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-all">
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700 mb-1">Umspannwerk ID</label>
|
||||
<input type="text" formControlName="substationId"
|
||||
class="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-all">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700 mb-1">Transformator ID</label>
|
||||
<input type="text" formControlName="transformerId"
|
||||
class="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-all">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="px-6 py-4 border-t border-slate-200 bg-slate-50 flex justify-end gap-3 shrink-0">
|
||||
<button type="button" (click)="closeModal()" class="px-5 py-2.5 text-sm font-medium text-slate-700 hover:bg-slate-200 rounded-xl transition-colors">
|
||||
Abbrechen
|
||||
</button>
|
||||
<button type="submit" form="createEcForm" [disabled]="!createForm.valid"
|
||||
class="px-5 py-2.5 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed rounded-xl transition-colors shadow-sm">
|
||||
Speichern
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
@ -0,0 +1,22 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AdminEnergyCommunity } from './admin-energy-community';
|
||||
|
||||
describe('AdminEnergyCommunity', () => {
|
||||
let component: AdminEnergyCommunity;
|
||||
let fixture: ComponentFixture<AdminEnergyCommunity>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [AdminEnergyCommunity],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(AdminEnergyCommunity);
|
||||
component = fixture.componentInstance;
|
||||
await fixture.whenStable();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,116 @@
|
||||
import {Component, inject, OnInit} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {FormBuilder, ReactiveFormsModule, Validators} from '@angular/forms';
|
||||
import {EnergyCommunity, EnergyCommunityAdminControllerService} from '../../api';
|
||||
|
||||
@Component({
|
||||
selector: 'app-admin-energy-community',
|
||||
imports: [CommonModule, ReactiveFormsModule],
|
||||
templateUrl: './admin-energy-community.html',
|
||||
styleUrl: './admin-energy-community.scss',
|
||||
})
|
||||
export class AdminEnergyCommunity implements OnInit {
|
||||
private ecService = inject(EnergyCommunityAdminControllerService);
|
||||
private fb = inject(FormBuilder);
|
||||
|
||||
communities: EnergyCommunity[] = [];
|
||||
|
||||
isModalOpen = false;
|
||||
isEditMode = false;
|
||||
editingId: string | null = null;
|
||||
|
||||
createForm = this.fb.group({
|
||||
name: ['', Validators.required],
|
||||
edaEcId: ['', Validators.required],
|
||||
type: ['', Validators.required],
|
||||
gridOperatorId: [''],
|
||||
substationId: [''],
|
||||
transformerId: ['']
|
||||
});
|
||||
|
||||
ngOnInit() {
|
||||
this.loadCommunities();
|
||||
}
|
||||
|
||||
loadCommunities() {
|
||||
this.ecService.getAllEnergyCommunities().subscribe({
|
||||
next: (data) => {
|
||||
this.communities = data;
|
||||
},
|
||||
error: (err) => {
|
||||
console.error('Fehler beim Laden der Energiegemeinschaften', err);
|
||||
// Hier würde später ein globaler Error-Toast getriggert werden
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
openCreateModal(): void {
|
||||
this.isEditMode = false;
|
||||
this.editingId = null;
|
||||
this.createForm.reset();
|
||||
this.createForm.get('edaEcId')?.enable();
|
||||
this.createForm.get('type')?.enable();
|
||||
|
||||
this.isModalOpen = true;
|
||||
}
|
||||
|
||||
openEditModal(ec: EnergyCommunity): void {
|
||||
this.isEditMode = true;
|
||||
this.editingId = ec.id ?? null;
|
||||
this.createForm.patchValue(ec as any);
|
||||
this.createForm.get('edaEcId')?.disable();
|
||||
this.createForm.get('type')?.disable();
|
||||
|
||||
this.isModalOpen = true;
|
||||
}
|
||||
|
||||
closeModal(): void {
|
||||
this.isModalOpen = false;
|
||||
this.createForm.reset();
|
||||
}
|
||||
|
||||
onSubmit(): void {
|
||||
if (this.createForm.valid) {
|
||||
const formData = this.createForm.getRawValue();
|
||||
|
||||
if (this.isEditMode && this.editingId) {
|
||||
this.ecService.updateEnergyCommunity(this.editingId, formData as any).subscribe({
|
||||
next: (updatedEc) => {
|
||||
const index = this.communities.findIndex(c => c.id === this.editingId);
|
||||
if (index !== -1) this.communities[index] = updatedEc;
|
||||
this.closeModal();
|
||||
},
|
||||
error: (err) => console.error('Fehler beim Updaten', err)
|
||||
});
|
||||
} else {
|
||||
this.ecService.createEnergyCommunity(formData as any).subscribe({
|
||||
next: (newCommunity) => {
|
||||
this.communities.push(newCommunity);
|
||||
this.closeModal();
|
||||
},
|
||||
error: (err) => console.error('Fehler beim Erstellen', err)
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.createForm.markAllAsTouched();
|
||||
}
|
||||
}
|
||||
|
||||
canDelete(ec: EnergyCommunity): boolean {
|
||||
// Wenn dein Backend z.B. die Anzahl der Mitglieder mitliefert:
|
||||
// return !(ec as any).memberCount || (ec as any).memberCount === 0;
|
||||
return true; // Vorerst true, bis die Relation im DTO steht
|
||||
}
|
||||
|
||||
onDelete(id: string | undefined): void {
|
||||
if (!id) return;
|
||||
if (confirm('Möchtest du diese Energiegemeinschaft wirklich löschen?')) {
|
||||
this.ecService.deleteEnergyCommunity(id).subscribe({
|
||||
next: () => {
|
||||
this.communities = this.communities.filter(c => c.id !== id);
|
||||
},
|
||||
error: (err) => console.error('Löschen fehlgeschlagen', err)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import {inject, Injectable} from '@angular/core';
|
||||
import {inject, Injectable, signal} from '@angular/core';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {BehaviorSubject, Observable, tap} from 'rxjs';
|
||||
import {AuthResponse} from '../api';
|
||||
@ -16,8 +16,9 @@ export class AuthService {
|
||||
|
||||
isLoggedIn$ = this.isLoggedInSubject.asObservable();
|
||||
|
||||
currentUserRole = signal<string | null>(localStorage.getItem('user_role'));
|
||||
|
||||
register(userData: any): Observable<any> {
|
||||
// Sendet die Daten als JSON-Body an den Endpunkt /register
|
||||
return this.http.post(`${this.apiUrl}/register`, userData);
|
||||
}
|
||||
|
||||
@ -27,13 +28,12 @@ export class AuthService {
|
||||
// Token und Rolle im LocalStorage (oder SessionStorage) ablegen
|
||||
localStorage.setItem('token', response.token!);
|
||||
localStorage.setItem('user_role', response.role!);
|
||||
this.isLoggedInSubject.next(true);
|
||||
this.currentUserRole.set(response.role!);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
getUserRole(): string | null {
|
||||
return localStorage.getItem('user_role');
|
||||
}
|
||||
|
||||
isLoggedIn(): boolean {
|
||||
return !!localStorage.getItem('token');
|
||||
|
||||
Loading…
Reference in New Issue
Block a user