Numbers, UserPush, UserPublic cleanup, PasswordComponent
This commit is contained in:
parent
d59ad4177f
commit
d9b84c5802
@ -1,5 +1,4 @@
|
||||
import {validateBoolean, validateDate, validateList, validateString} from "../common/validators";
|
||||
import {Group} from "../group/Group";
|
||||
import {validateBoolean, validateDate, validateString} from "../common/validators";
|
||||
|
||||
export class UserPrivate {
|
||||
|
||||
@ -9,7 +8,6 @@ export class UserPrivate {
|
||||
readonly created: Date,
|
||||
readonly name: string,
|
||||
readonly password: boolean,
|
||||
readonly groups: Group[],
|
||||
) {
|
||||
// -
|
||||
}
|
||||
@ -21,7 +19,6 @@ export class UserPrivate {
|
||||
validateDate(json['created']),
|
||||
validateString(json['name']),
|
||||
validateBoolean(json['password']),
|
||||
validateList(json['groups'], Group.fromJson),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -2,13 +2,23 @@ import {Injectable} from '@angular/core';
|
||||
import {ApiService} from "../common/api.service";
|
||||
import {UserPrivate} from "./UserPrivate";
|
||||
import {Next} from "../common/types";
|
||||
import {UserCommon} from "./UserCommon";
|
||||
import {UserPublic} from "./UserPublic";
|
||||
import {EventType, Router} from "@angular/router";
|
||||
import {BehaviorSubject, Subscription} from "rxjs";
|
||||
import {BehaviorSubject, filter, Subject, Subscription} from "rxjs";
|
||||
import {Group} from "../group/Group";
|
||||
import {StompService} from "@stomp/ng2-stompjs";
|
||||
import {Subscribed} from "../Subscribed";
|
||||
import {NumbersPrivateDto} from "../tools/Numbers/NumbersPrivateDto";
|
||||
|
||||
function userPushMessageFromJson(json: any): object {
|
||||
const type = json['_type_'];
|
||||
switch (type) {
|
||||
case 'NumbersPrivateDto':
|
||||
return NumbersPrivateDto.fromJson(json['payload']);
|
||||
case 'UserPrivateDto':
|
||||
return UserPrivate.fromJson(json['payload']);
|
||||
}
|
||||
throw new Error("Not implemented UserPushMessage._type_ = " + type);
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@ -17,7 +27,9 @@ export class UserService {
|
||||
|
||||
private readonly subject: BehaviorSubject<UserPrivate | null> = new BehaviorSubject<UserPrivate | null>(null);
|
||||
|
||||
private subscription?: Subscription;
|
||||
private readonly pushSubject: Subject<any> = new Subject<any>();
|
||||
|
||||
private userSubscriptions: Subscription[] = [];
|
||||
|
||||
get user(): UserPrivate | null {
|
||||
return this.subject.value;
|
||||
@ -30,15 +42,14 @@ export class UserService {
|
||||
) {
|
||||
this.router.events.subscribe(e => {
|
||||
if (e.type === EventType.NavigationEnd || e.type === EventType.NavigationSkipped) {
|
||||
console.log(e);
|
||||
this.refresh();
|
||||
}
|
||||
});
|
||||
this.stompService.connected$.subscribe(() => this.refresh());
|
||||
}
|
||||
|
||||
getCommonByUuid(uuid: string, next: Next<UserCommon>): void {
|
||||
this.api.postSingle(['User', 'getCommonByUuid'], uuid, UserCommon.fromJson, next);
|
||||
getByPublicUuid(publicUuid: any, next: Next<UserPublic>) {
|
||||
this.api.postSingle(['User', 'getByPublicUuid'], publicUuid, UserPublic.fromJson, next);
|
||||
}
|
||||
|
||||
delete(next?: Next<void>) {
|
||||
@ -54,7 +65,7 @@ export class UserService {
|
||||
}
|
||||
|
||||
goto(user: UserPublic) {
|
||||
this.router.navigate(['/User', user.publicUuid]);
|
||||
this.router.navigate(['User', user.publicUuid]);
|
||||
}
|
||||
|
||||
refresh() {
|
||||
@ -65,24 +76,25 @@ export class UserService {
|
||||
return this.subject.subscribe(next);
|
||||
}
|
||||
|
||||
subscribePush<T>(predicate: (m: any) => boolean, next: Next<T>): Subscription {
|
||||
return this.pushSubject
|
||||
.pipe(filter(predicate))
|
||||
.subscribe(next);
|
||||
}
|
||||
|
||||
iOwn(group: Group): boolean {
|
||||
return this.user?.publicUuid === group.owner.publicUuid;
|
||||
}
|
||||
|
||||
private setUser(user: UserPrivate | null) {
|
||||
if (this.subject.value?.privateUuid !== user?.privateUuid) {
|
||||
if (this.subscription) {
|
||||
this.subscription.unsubscribe();
|
||||
this.subscription = undefined;
|
||||
}
|
||||
if (user === null || this.subject.value?.privateUuid !== user.privateUuid) {
|
||||
this.userSubscriptions.forEach(subscription => subscription.unsubscribe());
|
||||
this.userSubscriptions.length = 0;
|
||||
if (user) {
|
||||
this.subscription = this.api.subscribe(["User", user.privateUuid], UserPrivate.fromJson, user => this.setUser(user));
|
||||
this.userSubscriptions.push(this.api.subscribe(["User", user.privateUuid], userPushMessageFromJson, message => this.pushSubject.next(message)));
|
||||
}
|
||||
}
|
||||
this.subject.next(user);
|
||||
}
|
||||
|
||||
newSubscriber(): Subscribed<UserPrivate> {
|
||||
return new Subscribed<UserPrivate>(UserPrivate.samePrivateUuid, (user, next) => this.api.subscribe(['UserPrivate', user.privateUuid], UserPrivate.fromJson, next));
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,6 +28,14 @@ export class GroupService {
|
||||
this.api.postSingle(['Group', 'get'], uuid, Group.fromJson, next);
|
||||
}
|
||||
|
||||
findAllCommon(uuid: string, next: Next<Group[]>): void {
|
||||
this.api.postList(['Group', 'findAllCommon'], uuid, Group.fromJson, next);
|
||||
}
|
||||
|
||||
findAllJoined(next: Next<Group[]>) {
|
||||
this.api.getList(['Group', 'findAllJoined'], Group.fromJson, next);
|
||||
}
|
||||
|
||||
create(next: Next<Group>): void {
|
||||
this.api.getSingle(['Group', 'create'], Group.fromJson, next);
|
||||
}
|
||||
@ -60,8 +68,8 @@ export class GroupService {
|
||||
this.api.postNone(['Group', 'leave'], group, next);
|
||||
}
|
||||
|
||||
goto(group: Group) {
|
||||
this.router.navigate(['Group', group.uuid]);
|
||||
goto(uuid: string) {
|
||||
this.router.navigate(['Group', uuid]);
|
||||
}
|
||||
|
||||
newSubscriber(): Subscribed<Group> {
|
||||
|
||||
@ -1,22 +0,0 @@
|
||||
import {validateString} from "../../common/validators";
|
||||
import {Group} from "../../group/Group";
|
||||
|
||||
export class Numbers {
|
||||
|
||||
constructor(
|
||||
readonly uuid: string,
|
||||
readonly name: string,
|
||||
readonly group: Group,
|
||||
) {
|
||||
// -
|
||||
}
|
||||
|
||||
static fromJson(json: any): Numbers {
|
||||
return new Numbers(
|
||||
validateString(json['uuid']),
|
||||
validateString(json['name']),
|
||||
Group.fromJson(json['group']),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
import {Group} from "../../group/Group";
|
||||
import {validateDate, validateNumber, validateString} from "../../common/validators";
|
||||
|
||||
export class NumbersPrivateDto {
|
||||
|
||||
constructor(
|
||||
readonly uuid: string,
|
||||
readonly group: Group,
|
||||
readonly date: Date,
|
||||
readonly number: number,
|
||||
) {
|
||||
// -
|
||||
}
|
||||
|
||||
static fromJson(json: any): NumbersPrivateDto {
|
||||
return new NumbersPrivateDto(
|
||||
validateString(json['uuid']),
|
||||
Group.fromJson(json['group']),
|
||||
validateDate(json['date']),
|
||||
validateNumber(json['number']),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@ -2,6 +2,9 @@ import {Injectable} from '@angular/core';
|
||||
import {ApiService} from "../../common/api.service";
|
||||
import {Router} from "@angular/router";
|
||||
import {UserService} from "../../User/user.service";
|
||||
import {Next} from "../../common/types";
|
||||
import {NumbersPrivateDto} from "./NumbersPrivateDto";
|
||||
import {validateBoolean, validateString} from "../../common/validators";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@ -13,7 +16,25 @@ export class NumbersService {
|
||||
protected readonly router: Router,
|
||||
protected readonly userService: UserService
|
||||
) {
|
||||
// -
|
||||
this.userService.subscribePush<NumbersPrivateDto>(m => m instanceof NumbersPrivateDto, message => {
|
||||
this.router.navigate(['Numbers', message.uuid])
|
||||
});
|
||||
}
|
||||
|
||||
create(groupUuid: string) {
|
||||
this.api.getNone(['Numbers', 'create', groupUuid]);
|
||||
}
|
||||
|
||||
byUuid(uuid: string, next: Next<NumbersPrivateDto>) {
|
||||
this.api.postSingle(['Numbers', 'byUuid'], uuid, NumbersPrivateDto.fromJson, next);
|
||||
}
|
||||
|
||||
canAccess(uuid: string, next: Next<boolean>) {
|
||||
this.api.postSingle(['Numbers', 'canAccess'], uuid, validateBoolean, next);
|
||||
}
|
||||
|
||||
getGroupUuid(uuid: string | null, next: Next<string>) {
|
||||
this.api.postSingle(['Numbers', 'getGroupUuid'], uuid, validateString, next);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<div id="mainMenu" *ngIf="menuVisible">
|
||||
<div class="mainMenuItem" routerLinkActive="mainMenuItemActive" routerLink="/SolarSystem">Planeten</div>
|
||||
<div class="mainMenuItem" routerLinkActive="mainMenuItemActive" routerLink="/VoltageDrop">Leitung</div>
|
||||
<div class="mainMenuItem" routerLinkActive="mainMenuItemActive" routerLink="/VoltageDrop">Kabel</div>
|
||||
<div class="mainMenuItem" routerLinkActive="mainMenuItemActive" routerLink="/Groups">Gruppen</div>
|
||||
<ng-container *ngIf="user !== null">
|
||||
<div class="mainMenuItem mainMenuItemRight" routerLinkActive="mainMenuItemActive" routerLink="/Profile">{{ user.name }}</div>
|
||||
|
||||
@ -2,7 +2,6 @@ import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||
import {Router, RouterLink, RouterLinkActive, RouterOutlet} from '@angular/router';
|
||||
import {NgIf} from "@angular/common";
|
||||
import {UserService} from "./api/User/user.service";
|
||||
import {Numbers} from "./api/tools/Numbers/Numbers";
|
||||
import {UserPrivate} from "./api/User/UserPrivate";
|
||||
import {Subscription} from "rxjs";
|
||||
|
||||
@ -15,8 +14,6 @@ import {Subscription} from "rxjs";
|
||||
})
|
||||
export class AppComponent implements OnInit, OnDestroy {
|
||||
|
||||
protected readonly NumbersGroup = Numbers;
|
||||
|
||||
private readonly subscriptions: Subscription[] = [];
|
||||
|
||||
protected menuVisible = true;
|
||||
@ -37,7 +34,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
ngOnInit(): void {
|
||||
this.subscriptions.push(this.userService.subscribe(user => {
|
||||
if (this.user !== null && user === null) {
|
||||
this.router.navigate(['/']);
|
||||
this.router.navigate(['']);
|
||||
}
|
||||
this.user = user;
|
||||
}));
|
||||
@ -47,8 +44,4 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
this.subscriptions.forEach(subscription => subscription.unsubscribe());
|
||||
}
|
||||
|
||||
delete() {
|
||||
this.userService.delete();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import {ProfileComponent} from "./pages/profile/profile.component";
|
||||
import {UserComponent} from "./pages/user/user.component";
|
||||
import {GroupsComponent} from "./pages/group/groups/groups.component";
|
||||
import {GroupComponent} from "./pages/group/group/group.component";
|
||||
import {NumbersComponent} from "./pages/tools/numbers/numbers.component";
|
||||
|
||||
export const routes: Routes = [
|
||||
{path: 'SolarSystemPrintout', component: SolarSystemPrintoutComponent},
|
||||
@ -16,6 +17,8 @@ export const routes: Routes = [
|
||||
{path: 'Groups', component: GroupsComponent},
|
||||
{path: 'Group/:uuid', component: GroupComponent},
|
||||
|
||||
{path: 'Numbers/:uuid', component: NumbersComponent},
|
||||
|
||||
{path: 'User/:publicUuid', component: UserComponent},
|
||||
|
||||
{path: 'Profile', component: ProfileComponent},
|
||||
|
||||
@ -26,10 +26,9 @@
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<button (click)="numbersService.create(group.value.uuid)">Numbers</button>
|
||||
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="uuid && !group.value && accessDenied">
|
||||
<h1>Passwort</h1>
|
||||
<input type="text" [(ngModel)]="password" (keydown.enter)="join()">
|
||||
<button (click)="join()">Beitreten</button>
|
||||
</ng-container>
|
||||
<app-password [visible]="granted === false" (join)="join($event)"></app-password>
|
||||
|
||||
@ -7,6 +7,8 @@ import {GroupService} from "../../../api/group/group.service";
|
||||
import {UserService} from "../../../api/User/user.service";
|
||||
import {Group} from "../../../api/group/Group";
|
||||
import {Subscribed} from "../../../api/Subscribed";
|
||||
import {NumbersService} from "../../../api/tools/Numbers/numbers.service";
|
||||
import {PasswordComponent} from "../../../shared/password/password.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-group',
|
||||
@ -17,7 +19,8 @@ import {Subscribed} from "../../../api/Subscribed";
|
||||
NgIf,
|
||||
ReactiveFormsModule,
|
||||
TextComponent,
|
||||
FormsModule
|
||||
FormsModule,
|
||||
PasswordComponent
|
||||
],
|
||||
templateUrl: './group.component.html',
|
||||
styleUrl: './group.component.less'
|
||||
@ -26,43 +29,36 @@ export class GroupComponent implements OnInit {
|
||||
|
||||
protected readonly group: Subscribed<Group>;
|
||||
|
||||
protected granted: boolean | null = null;
|
||||
|
||||
protected uuid: string | null = null;
|
||||
|
||||
protected accessDenied: boolean | null = null;
|
||||
|
||||
protected password: string = "";
|
||||
|
||||
constructor(
|
||||
protected readonly router: Router,
|
||||
protected readonly activatedRoute: ActivatedRoute,
|
||||
protected readonly groupService: GroupService,
|
||||
protected readonly userService: UserService,
|
||||
protected readonly numbersService: NumbersService,
|
||||
) {
|
||||
this.group = this.groupService.newSubscriber();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.password = "";
|
||||
this.accessDenied = null;
|
||||
this.activatedRoute.params.subscribe(params => {
|
||||
this.uuid = params['uuid'];
|
||||
if (this.uuid) {
|
||||
this.groupService.canAccess(this.uuid, canAccess => {
|
||||
this.accessDenied = !canAccess;
|
||||
if (canAccess && this.uuid) {
|
||||
this.groupService.get(this.uuid, group => this.group.value = group);
|
||||
const uuid = params['uuid'];
|
||||
this.uuid = uuid;
|
||||
this.granted = null;
|
||||
if (uuid) {
|
||||
this.groupService.canAccess(uuid, granted => {
|
||||
this.granted = granted;
|
||||
if (granted) {
|
||||
this.groupService.get(uuid, group => this.group.value = group);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected join() {
|
||||
if (this.uuid) {
|
||||
this.groupService.join(this.uuid, this.password, group => this.group.value = group);
|
||||
}
|
||||
}
|
||||
|
||||
protected changeTitle(group: Group, title: string) {
|
||||
this.groupService.changeTitle(group, title, group => this.group.value = group);
|
||||
}
|
||||
@ -71,4 +67,10 @@ export class GroupComponent implements OnInit {
|
||||
this.groupService.changePassword(group, password, group => this.group.value = group);
|
||||
}
|
||||
|
||||
protected join(password: string) {
|
||||
if (this.uuid) {
|
||||
this.groupService.join(this.uuid, password, group => this.group.value = group);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
</div>
|
||||
<div class="tileContent">
|
||||
<ng-container *ngIf="userService.user">
|
||||
<app-group-list [groups]="userService.user.groups"></app-group-list>
|
||||
<app-group-list [groups]="groups"></app-group-list>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="tileFooter">
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||
import {GroupListComponent} from "../shared/group-list/group-list.component";
|
||||
import {UserService} from "../../../api/User/user.service";
|
||||
import {NgIf} from "@angular/common";
|
||||
import {GroupService} from "../../../api/group/group.service";
|
||||
import {Group} from "../../../api/group/Group";
|
||||
import {Subscription} from "rxjs";
|
||||
|
||||
@Component({
|
||||
selector: 'app-groups',
|
||||
@ -14,7 +16,11 @@ import {GroupService} from "../../../api/group/group.service";
|
||||
templateUrl: './groups.component.html',
|
||||
styleUrl: './groups.component.less'
|
||||
})
|
||||
export class GroupsComponent {
|
||||
export class GroupsComponent implements OnInit, OnDestroy {
|
||||
|
||||
private readonly subscriptions: Subscription[] = [];
|
||||
|
||||
protected groups: Group[] = [];
|
||||
|
||||
constructor(
|
||||
protected readonly userService: UserService,
|
||||
@ -23,8 +29,18 @@ export class GroupsComponent {
|
||||
// -
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.subscriptions.push(this.userService.subscribe(_ => {
|
||||
this.groupService.findAllJoined(groups => this.groups = groups);
|
||||
}));
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.subscriptions.forEach(subscription => subscription.unsubscribe());
|
||||
}
|
||||
|
||||
create(): void {
|
||||
this.groupService.create(group => this.groupService.goto(group));
|
||||
this.groupService.create(group => this.groupService.goto(group.uuid));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<th>Titel</th>
|
||||
<th>Teilnehmer</th>
|
||||
</tr>
|
||||
<tr *ngFor="let group of sorted()" (click)="groupService.goto(group)">
|
||||
<tr *ngFor="let group of sorted()" (click)="groupService.goto(group.uuid)">
|
||||
<td class="title">{{ group.title }}</td>
|
||||
<td class="users">{{ group.users.length }}</td>
|
||||
</tr>
|
||||
|
||||
@ -30,7 +30,7 @@ export class GroupListComponent {
|
||||
}
|
||||
|
||||
protected create() {
|
||||
this.groupService.create(group => this.groupService.goto(group));
|
||||
this.groupService.create(group => this.groupService.goto(group.uuid));
|
||||
}
|
||||
|
||||
protected sorted(): Group[] {
|
||||
|
||||
@ -8,13 +8,15 @@
|
||||
<td>
|
||||
<app-text [initial]="userService.user.name" [editable]="true" (onChange)="userService.changeName($event)" [validator]="nameValidator"></app-text>
|
||||
</td>
|
||||
<td class="hint">
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="hint" colspan="2">
|
||||
Mindestens {{ USER_NAME_MIN_LENGTH }} Zeichen. Keine Leerzeichen. Buchstaben oder Zahlen müssen enthalten sein.
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th colspan="3"> </th>
|
||||
<th colspan="2"> </th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
@ -33,9 +35,6 @@
|
||||
<td>
|
||||
<input #p0 type="text" [(ngModel)]="password0" [class.passwordInvalid]="p0Invalid()" [class.passwordValid]="p0Valid()" (keydown.enter)="p0Enter()">
|
||||
</td>
|
||||
<td class="hint">
|
||||
Mindestens {{ USER_PASSWORD_MIN_LENGTH }} Zeichen. Nicht nur Zahlen. Nicht nur Buchstaben.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
@ -44,17 +43,19 @@
|
||||
<td>
|
||||
<input #p1 type="text" [(ngModel)]="password1" [class.passwordInvalid]="p1Invalid()" [class.passwordValid]="p1Valid()" (keydown.enter)="p1Enter()">
|
||||
</td>
|
||||
<td class="hint">
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="hint" colspan="2">
|
||||
Mindestens {{ USER_PASSWORD_MIN_LENGTH }} Zeichen. Nicht nur Zahlen. Nicht nur Buchstaben.
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th colspan="3"> </th>
|
||||
<th colspan="2"> </th>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
<app-group-list [groups]="userService.user.groups"></app-group-list>
|
||||
<app-group-list [groups]="groups"></app-group-list>
|
||||
|
||||
</ng-container>
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
}
|
||||
@ -12,6 +16,7 @@ th {
|
||||
|
||||
input {
|
||||
-webkit-text-security: disc;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.passwordInvalid {
|
||||
@ -21,3 +26,8 @@ input {
|
||||
.passwordValid {
|
||||
background-color: lightgreen;
|
||||
}
|
||||
|
||||
.hint {
|
||||
font-size: 80%;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
import {Component, ElementRef, ViewChild} from '@angular/core';
|
||||
import {Component, ElementRef, OnDestroy, OnInit, ViewChild} from '@angular/core';
|
||||
import {NgForOf, NgIf} from "@angular/common";
|
||||
import {UserService} from "../../api/User/user.service";
|
||||
import {FormsModule} from "@angular/forms";
|
||||
import {TextComponent} from "../../shared/text/text.component";
|
||||
import {GroupListComponent} from "../group/shared/group-list/group-list.component";
|
||||
import {Group} from "../../api/group/Group";
|
||||
import {Subscription} from "rxjs";
|
||||
import {GroupService} from "../../api/group/group.service";
|
||||
|
||||
const USER_NAME_MIN_LENGTH = 2;
|
||||
|
||||
@ -22,27 +25,41 @@ const USER_PASSWORD_MIN_LENGTH = 10;
|
||||
templateUrl: './profile.component.html',
|
||||
styleUrl: './profile.component.less'
|
||||
})
|
||||
export class ProfileComponent {
|
||||
export class ProfileComponent implements OnInit, OnDestroy {
|
||||
|
||||
protected readonly USER_NAME_MIN_LENGTH = USER_NAME_MIN_LENGTH;
|
||||
|
||||
protected readonly USER_PASSWORD_MIN_LENGTH = USER_PASSWORD_MIN_LENGTH;
|
||||
|
||||
private readonly subscriptions: Subscription[] = [];
|
||||
|
||||
protected password0: string = "";
|
||||
|
||||
protected password1: string = "";
|
||||
|
||||
protected groups: Group[] = [];
|
||||
|
||||
@ViewChild('p1')
|
||||
p1!: ElementRef;
|
||||
|
||||
constructor(
|
||||
protected readonly userService: UserService,
|
||||
protected readonly groupService: GroupService,
|
||||
) {
|
||||
// -
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.subscriptions.push(this.userService.subscribe(_ => {
|
||||
this.groupService.findAllJoined(groups => this.groups = groups);
|
||||
}));
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.subscriptions.forEach(subscription => subscription.unsubscribe());
|
||||
}
|
||||
|
||||
protected nameValidator(name: string): boolean {
|
||||
console.log(name);
|
||||
return name.length >= USER_NAME_MIN_LENGTH && !/\s+|^[^a-zA-Z0-9]+$/.test(name);
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
<div
|
||||
*ngIf="numbers"
|
||||
class="numbers"
|
||||
[class.date_middle]="now.getTime() - numbers.date.getTime() >= 30 * 1000"
|
||||
[class.date_old]="now.getTime() - numbers.date.getTime() >= 5 * 60 * 1000"
|
||||
>
|
||||
|
||||
<button *ngIf="userService.iOwn(numbers.group)" (click)="numbersService.create(numbers.group.uuid)">
|
||||
Nächste Runde
|
||||
</button>
|
||||
|
||||
<div class="date">
|
||||
{{ numbers.date | relative:now }}
|
||||
</div>
|
||||
|
||||
<div class="huge">
|
||||
{{ numbers.number }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@ -0,0 +1,21 @@
|
||||
.huge {
|
||||
font-size: 50vmin;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.date {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.numbers {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.date_middle {
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.date_old {
|
||||
color: red;
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||
import {NumbersPrivateDto} from "../../../api/tools/Numbers/NumbersPrivateDto";
|
||||
import {ActivatedRoute} from "@angular/router";
|
||||
import {NumbersService} from "../../../api/tools/Numbers/numbers.service";
|
||||
import {NgIf} from "@angular/common";
|
||||
import {FormsModule} from "@angular/forms";
|
||||
import {PasswordComponent} from "../../../shared/password/password.component";
|
||||
import {GroupService} from "../../../api/group/group.service";
|
||||
import {UserService} from "../../../api/User/user.service";
|
||||
import {RelativePipe} from "../../../shared/relative.pipe";
|
||||
import {Subscription, timer} from "rxjs";
|
||||
|
||||
@Component({
|
||||
selector: 'app-numbers',
|
||||
standalone: true,
|
||||
imports: [
|
||||
NgIf,
|
||||
FormsModule,
|
||||
PasswordComponent,
|
||||
RelativePipe
|
||||
],
|
||||
templateUrl: './numbers.component.html',
|
||||
styleUrl: './numbers.component.less'
|
||||
})
|
||||
export class NumbersComponent implements OnInit, OnDestroy {
|
||||
|
||||
protected numbers: NumbersPrivateDto | null = null;
|
||||
|
||||
protected now: Date = new Date();
|
||||
|
||||
private timer?: Subscription;
|
||||
|
||||
constructor(
|
||||
protected readonly activatedRoute: ActivatedRoute,
|
||||
protected readonly numbersService: NumbersService,
|
||||
protected readonly userService: UserService,
|
||||
protected readonly groupService: GroupService,
|
||||
) {
|
||||
// -
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.timer = timer(1000, 1000).subscribe(() => this.now = new Date());
|
||||
this.activatedRoute.params.subscribe(params => {
|
||||
const uuid = params['uuid'];
|
||||
if (uuid) {
|
||||
this.numbersService.canAccess(uuid, granted => {
|
||||
if (granted) {
|
||||
this.numbersService.byUuid(uuid, numbers => this.numbers = numbers);
|
||||
} else {
|
||||
this.numbersService.getGroupUuid(uuid, groupUuid => this.groupService.goto(groupUuid));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.numbers = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.timer) {
|
||||
this.timer.unsubscribe();
|
||||
this.timer = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
<ng-container *ngIf="user !== null">
|
||||
<h2>Benutzer: {{ user.name }}</h2>
|
||||
|
||||
<h3>Gemeinsame Sitzungen</h3>
|
||||
<app-group-list [groups]="user.commonGroups"></app-group-list>
|
||||
<h3>Gemeinsame Gruppen</h3>
|
||||
<app-group-list [groups]="commonGroups"></app-group-list>
|
||||
</ng-container>
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {NgForOf, NgIf} from "@angular/common";
|
||||
import {ActivatedRoute} from "@angular/router";
|
||||
import {UserService} from "../../api/User/user.service";
|
||||
import {GroupListComponent} from "../group/shared/group-list/group-list.component";
|
||||
import {UserCommon} from "../../api/User/UserCommon";
|
||||
import {GroupService} from "../../api/group/group.service";
|
||||
import {UserService} from "../../api/User/user.service";
|
||||
import {Group} from "../../api/group/Group";
|
||||
import {UserPublic} from "../../api/User/UserPublic";
|
||||
|
||||
@Component({
|
||||
selector: 'app-user',
|
||||
@ -18,11 +20,14 @@ import {UserCommon} from "../../api/User/UserCommon";
|
||||
})
|
||||
export class UserComponent implements OnInit {
|
||||
|
||||
protected user: UserCommon | null = null;
|
||||
protected user: UserPublic | null = null;
|
||||
|
||||
protected commonGroups: Group[] = [];
|
||||
|
||||
constructor(
|
||||
protected readonly activatedRoute: ActivatedRoute,
|
||||
protected readonly userService: UserService,
|
||||
protected readonly groupService: GroupService,
|
||||
) {
|
||||
// -
|
||||
}
|
||||
@ -31,7 +36,8 @@ export class UserComponent implements OnInit {
|
||||
this.activatedRoute.params.subscribe(params => {
|
||||
const publicUuid = params['publicUuid'];
|
||||
if (publicUuid) {
|
||||
this.userService.getCommonByUuid(publicUuid, user => this.user = user);
|
||||
this.userService.getByPublicUuid(publicUuid, user => this.user = user);
|
||||
this.groupService.findAllCommon(publicUuid, commonGroups => this.commonGroups = commonGroups);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
<ng-container *ngIf="visible">
|
||||
<h1>Passwort</h1>
|
||||
<input type="text" [(ngModel)]="password" (keydown.enter)="join.emit(password)">
|
||||
<button (click)="join.emit(password)">Beitreten</button>
|
||||
</ng-container>
|
||||
@ -0,0 +1,29 @@
|
||||
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
|
||||
import {FormsModule} from "@angular/forms";
|
||||
import {NgIf} from "@angular/common";
|
||||
|
||||
@Component({
|
||||
selector: 'app-password',
|
||||
standalone: true,
|
||||
imports: [
|
||||
FormsModule,
|
||||
NgIf
|
||||
],
|
||||
templateUrl: './password.component.html',
|
||||
styleUrl: './password.component.less'
|
||||
})
|
||||
export class PasswordComponent implements OnInit {
|
||||
|
||||
@Input()
|
||||
visible: boolean = false;
|
||||
|
||||
@Output()
|
||||
join: EventEmitter<string> = new EventEmitter();
|
||||
|
||||
protected password: string = "";
|
||||
|
||||
ngOnInit(): void {
|
||||
this.password = "";
|
||||
}
|
||||
|
||||
}
|
||||
33
src/main/angular/src/app/shared/relative.pipe.ts
Normal file
33
src/main/angular/src/app/shared/relative.pipe.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
|
||||
@Pipe({
|
||||
name: 'relative',
|
||||
standalone: true
|
||||
})
|
||||
export class RelativePipe implements PipeTransform {
|
||||
|
||||
transform(value: Date | null | undefined, now: Date): unknown {
|
||||
if (value === null || value === undefined) {
|
||||
return value;
|
||||
}
|
||||
const totalMillis = now.getTime() - value.getTime();
|
||||
const totalSeconds = Math.floor(totalMillis / 1000);
|
||||
const totalMinutes = Math.floor(totalSeconds / 60);
|
||||
const totalHours = Math.floor(totalMinutes / 60);
|
||||
const days = Math.floor(totalHours / 24);
|
||||
const seconds = totalSeconds % 60;
|
||||
const minutes = totalMinutes % 60;
|
||||
const hours = totalHours % 24;
|
||||
if (totalSeconds <= 0) {
|
||||
return "Gerade eben";
|
||||
} else if (days > 0) {
|
||||
return `Vor ${days} Tag${days === 1 ? '' : 'e'}`;
|
||||
} else if (hours > 0) {
|
||||
return `Vor ${hours} Stunde${hours === 1 ? '' : 'n'}`;
|
||||
} else if (minutes > 0) {
|
||||
return `Vor ${minutes} Minute${minutes === 1 ? '' : 'n'}`;
|
||||
}
|
||||
return `Vor ${seconds} Sekunde${seconds === 1 ? '' : 'n'}`
|
||||
}
|
||||
|
||||
}
|
||||
@ -7,3 +7,7 @@
|
||||
.invalid {
|
||||
background-color: indianred;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
13
src/main/java/de/ph87/tools/group/Access.java
Normal file
13
src/main/java/de/ph87/tools/group/Access.java
Normal file
@ -0,0 +1,13 @@
|
||||
package de.ph87.tools.group;
|
||||
|
||||
import de.ph87.tools.user.User;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Access {
|
||||
|
||||
public final User user;
|
||||
|
||||
public final Group group;
|
||||
|
||||
}
|
||||
@ -68,6 +68,7 @@ public class Group implements IWebSocketMessage {
|
||||
return List.of("Number", uuid);
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
public boolean isOwnedBy(@NonNull final User user) {
|
||||
return owner.equals(user);
|
||||
}
|
||||
|
||||
53
src/main/java/de/ph87/tools/group/GroupAccessService.java
Normal file
53
src/main/java/de/ph87/tools/group/GroupAccessService.java
Normal file
@ -0,0 +1,53 @@
|
||||
package de.ph87.tools.group;
|
||||
|
||||
import de.ph87.tools.user.User;
|
||||
import de.ph87.tools.user.UserService;
|
||||
import jakarta.annotation.Nullable;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class GroupAccessService {
|
||||
|
||||
private final GroupRepository groupRepository;
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
public boolean canAccess(@NonNull final String groupUuid, @Nullable final String userPrivateUuid) {
|
||||
final User user = userService.accessOrNull(userPrivateUuid);
|
||||
if (user == null) {
|
||||
return false;
|
||||
}
|
||||
final Group group = getByUuidOrThrow(groupUuid);
|
||||
return group.getUsers().contains(user);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Access access(@NonNull final String groupUuid, @NonNull final String userPrivateUuid) {
|
||||
final User user = userService.access(userPrivateUuid);
|
||||
final Group group = getByUuidOrThrow(groupUuid);
|
||||
return new Access(user, group);
|
||||
}
|
||||
|
||||
public Access accessAsOwner(@NonNull final String userPrivateUuid, @NonNull final String groupUuid) {
|
||||
final Access access = access(groupUuid, userPrivateUuid);
|
||||
if (!access.group.isOwnedBy(access.user)) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
return access;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Group getByUuidOrThrow(@NonNull final String groupUuid) {
|
||||
return groupRepository.findById(groupUuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
|
||||
}
|
||||
|
||||
}
|
||||
@ -6,6 +6,8 @@ import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static de.ph87.tools.UserArgumentResolver.USER_UUID_COOKIE_NAME;
|
||||
|
||||
@CrossOrigin
|
||||
@ -16,6 +18,8 @@ public class GroupController {
|
||||
|
||||
private final GroupService groupService;
|
||||
|
||||
private final GroupAccessService groupAccessService;
|
||||
|
||||
@GetMapping("create")
|
||||
public GroupDto create(@CookieValue(name = USER_UUID_COOKIE_NAME, required = false) @Nullable final String privateUuid, @NonNull final HttpServletResponse response) {
|
||||
return groupService.create(privateUuid, response);
|
||||
@ -23,7 +27,7 @@ public class GroupController {
|
||||
|
||||
@PostMapping("canAccess")
|
||||
public boolean canAccess(@CookieValue(name = USER_UUID_COOKIE_NAME, required = false) @Nullable final String privateUuid, @NonNull @RequestBody final String groupUuid) {
|
||||
return groupService.canAccess(privateUuid, groupUuid);
|
||||
return groupAccessService.canAccess(groupUuid, privateUuid);
|
||||
}
|
||||
|
||||
@PostMapping("get")
|
||||
@ -31,9 +35,21 @@ public class GroupController {
|
||||
return groupService.get(privateUuid, groupUuid);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@GetMapping("findAllJoined")
|
||||
public Set<GroupDto> findAllJoined(@CookieValue(name = USER_UUID_COOKIE_NAME) @NonNull final String userUuid) {
|
||||
return groupService.findAllJoined(userUuid);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@PostMapping("findAllCommon")
|
||||
public Set<GroupDto> findAllCommon(@CookieValue(name = USER_UUID_COOKIE_NAME) @NonNull final String userUuid, @NonNull @RequestBody final String targetUuid) {
|
||||
return groupService.findAllCommon(userUuid, targetUuid);
|
||||
}
|
||||
|
||||
@PostMapping("join")
|
||||
public GroupDto join(@CookieValue(name = USER_UUID_COOKIE_NAME) @NonNull final String privateUuid, @NonNull @RequestBody final GroupJoinRequest request) {
|
||||
return groupService.join(privateUuid, request);
|
||||
public GroupDto join(@CookieValue(name = USER_UUID_COOKIE_NAME, required = false) @Nullable final String privateUuid, @NonNull @RequestBody final GroupJoinRequest request, @NonNull final HttpServletResponse response) {
|
||||
return groupService.join(privateUuid, request, response);
|
||||
}
|
||||
|
||||
@PostMapping("changeTitle")
|
||||
|
||||
@ -1,19 +1,16 @@
|
||||
package de.ph87.tools.group;
|
||||
|
||||
import de.ph87.tools.user.UserPublicDto;
|
||||
import de.ph87.tools.web.IWebSocketMessage;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
public class GroupDto implements IWebSocketMessage {
|
||||
public class GroupDto {
|
||||
|
||||
@NonNull
|
||||
public final String uuid;
|
||||
@ -35,19 +32,14 @@ public class GroupDto implements IWebSocketMessage {
|
||||
|
||||
public final boolean initial;
|
||||
|
||||
public GroupDto(@NonNull final Group group, @NonNull final UserPublicDto owner) {
|
||||
public GroupDto(@NonNull final Group group, @NonNull final UserPublicDto owner, @NonNull final Set<UserPublicDto> users) {
|
||||
this.uuid = group.getUuid();
|
||||
this.title = group.getTitle();
|
||||
this.created = group.getCreated();
|
||||
this.password = group.getPassword();
|
||||
this.owner = owner;
|
||||
this.users = group.getUsers().stream().map(UserPublicDto::new).collect(Collectors.toSet());
|
||||
this.users = users;
|
||||
this.initial = group.isInitial();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> getWebsocketTopic() {
|
||||
return List.of("Group", uuid);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,57 +0,0 @@
|
||||
package de.ph87.tools.group;
|
||||
|
||||
import de.ph87.tools.user.User;
|
||||
import de.ph87.tools.user.UserPublicDto;
|
||||
import de.ph87.tools.user.UserPublicMapper;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class GroupMapper {
|
||||
|
||||
private final UserPublicMapper userPublicMapper;
|
||||
|
||||
private final GroupRepository groupRepository;
|
||||
|
||||
private final ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
/* DTO ------------------------------------------------------------------------------------------ */
|
||||
|
||||
@NonNull
|
||||
public GroupDto toDto(@NonNull final Group group) {
|
||||
final UserPublicDto owner = userPublicMapper.toPublicDto(group.getOwner());
|
||||
return new GroupDto(group, owner);
|
||||
}
|
||||
|
||||
/* PUBLISH -------------------------------------------------------------------------------------- */
|
||||
|
||||
@NonNull
|
||||
public GroupDto publish(@NonNull final Group group) {
|
||||
final GroupDto dto = toDto(group);
|
||||
applicationEventPublisher.publishEvent(dto);
|
||||
return dto;
|
||||
}
|
||||
|
||||
/* FIND & GET ----------------------------------------------------------------------------------- */
|
||||
|
||||
@NonNull
|
||||
public Set<GroupDto> findAllByUser(@NonNull final User user) {
|
||||
return groupRepository.findAllByUsersContains(user).stream().map(this::toDto).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Set<GroupDto> findAllCommonGroups(@NonNull final User a, @NonNull final User b) {
|
||||
return groupRepository.findAllByUsersContainsAndUsersContains(a, b).stream().map(this::toDto).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,52 +0,0 @@
|
||||
package de.ph87.tools.group;
|
||||
|
||||
import de.ph87.tools.user.User;
|
||||
import de.ph87.tools.user.UserService;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class GroupOfUserService {
|
||||
|
||||
private final GroupRepository groupRepository;
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
@NonNull
|
||||
public Group getGroupOfUser(@NonNull final String groupUuid, @NonNull final User user) {
|
||||
return findGroupOfUser(groupUuid, user).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Optional<Group> findGroupOfUser(@NonNull final String groupUuid, @NonNull final User user) {
|
||||
return groupRepository.findByUuidAndUsersContains(groupUuid, user);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public GroupOfUser accessGroupOfUser(@NonNull final String groupUuid, @NonNull final String privateUserUuid) {
|
||||
final User user = userService.getByPrivateUuidOrThrow(privateUserUuid);
|
||||
final Group group = getGroupOfUser(groupUuid, user);
|
||||
return new GroupOfUser(user, group);
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class GroupOfUser {
|
||||
|
||||
public final User user;
|
||||
|
||||
public final Group group;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -12,9 +12,6 @@ public interface GroupRepository extends ListCrudRepository<Group, String> {
|
||||
@NonNull
|
||||
Optional<Group> findByUuid(@NonNull String uuid);
|
||||
|
||||
@NonNull
|
||||
Optional<Group> findByUuidAndUsersContains(final @NonNull String groupUuid, @NonNull User user);
|
||||
|
||||
@NonNull
|
||||
Set<Group> findAllByUsersContains(@NonNull User user);
|
||||
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
package de.ph87.tools.group;
|
||||
|
||||
import de.ph87.tools.user.User;
|
||||
import de.ph87.tools.user.UserPublicMapper;
|
||||
import de.ph87.tools.user.UserPublicDto;
|
||||
import de.ph87.tools.user.UserService;
|
||||
import de.ph87.tools.user.push.UserPushService;
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.NonNull;
|
||||
@ -13,22 +14,23 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class GroupService {
|
||||
|
||||
private final GroupAccessService groupAccessService;
|
||||
|
||||
private final GroupRepository groupRepository;
|
||||
|
||||
private final UserPushService userPushService;
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
private final GroupMapper groupMapper;
|
||||
|
||||
private final UserPublicMapper userPublicMapper;
|
||||
|
||||
private final GroupOfUserService groupOfUserService;
|
||||
|
||||
@NonNull
|
||||
public GroupDto create(@Nullable final String privateUuid, @NonNull final HttpServletResponse response) {
|
||||
final User user = userService.getUserByPrivateUuidOrElseCreate(privateUuid, response);
|
||||
@ -36,22 +38,15 @@ public class GroupService {
|
||||
return doJoinUnchecked(group, user);
|
||||
}
|
||||
|
||||
public boolean canAccess(@Nullable final String privateUuid, @NonNull final String groupUuid) {
|
||||
if (privateUuid == null) {
|
||||
return false;
|
||||
}
|
||||
return userService.findByPrivateUuid(privateUuid).flatMap(user -> groupOfUserService.findGroupOfUser(groupUuid, user)).isPresent();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public GroupDto get(@NonNull final String privateUuid, @NonNull final String groupUuid) {
|
||||
final GroupOfUserService.GroupOfUser ug = groupOfUserService.accessGroupOfUser(groupUuid, privateUuid);
|
||||
return groupMapper.toDto(ug.group);
|
||||
final Access ug = groupAccessService.access(groupUuid, privateUuid);
|
||||
return toDto(ug.group);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public GroupDto join(@NonNull final String privateUuid, @NonNull final GroupJoinRequest request) {
|
||||
final User user = userService.getByPrivateUuidOrThrow(privateUuid);
|
||||
public GroupDto join(@Nullable final String privateUuid, @NonNull final GroupJoinRequest request, @NonNull final HttpServletResponse response) {
|
||||
final User user = userService.getUserByPrivateUuidOrElseCreate(privateUuid, response);
|
||||
final Group group = getByGroupByGroupUuid(request.uuid);
|
||||
if (!group.getPassword().equals(request.password)) {
|
||||
log.error("Wrong password: user={}, group={}", user, group);
|
||||
@ -67,27 +62,23 @@ public class GroupService {
|
||||
|
||||
@NonNull
|
||||
public GroupDto changeTitle(@NonNull final String privateUuid, @NonNull final GroupChangeTitleRequest request) {
|
||||
final GroupOfUserService.GroupOfUser ug = groupOfUserService.accessGroupOfUser(request.uuid, privateUuid);
|
||||
final Access ug = groupAccessService.access(request.uuid, privateUuid);
|
||||
if (!ug.group.isOwnedBy(ug.user)) {
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
|
||||
}
|
||||
ug.group.setTitle(request.title);
|
||||
return groupMapper.publish(ug.group);
|
||||
return publish(ug.group);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public GroupDto changePassword(@NonNull final String privateUuid, @NonNull final GroupChangePasswordInbound request) {
|
||||
final User user = userService.getByPrivateUuidOrThrow(privateUuid);
|
||||
final Group group = groupOfUserService.getGroupOfUser(request.uuid, user);
|
||||
if (!group.isOwnedBy(user)) {
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
|
||||
}
|
||||
group.setPassword(request.password);
|
||||
return groupMapper.publish(group);
|
||||
final Access access = groupAccessService.accessAsOwner(privateUuid, request.uuid);
|
||||
access.group.setPassword(request.password);
|
||||
return publish(access.group);
|
||||
}
|
||||
|
||||
public void leave(@NonNull final String privateUuid, @NonNull final String groupUuid) {
|
||||
final User user = userService.getByPrivateUuidOrThrow(privateUuid);
|
||||
final User user = userService.access(privateUuid);
|
||||
final Group group = getByGroupByGroupUuid(groupUuid);
|
||||
doLeaveUnchecked(group, user);
|
||||
}
|
||||
@ -107,9 +98,7 @@ public class GroupService {
|
||||
group.touch();
|
||||
user.touch();
|
||||
log.info("User joined Group: user={}, group={}", user, group);
|
||||
final GroupDto groupDto = groupMapper.publish(group);
|
||||
userPublicMapper.publish(user);
|
||||
return groupDto;
|
||||
return publish(group);
|
||||
}
|
||||
|
||||
private void doLeaveUnchecked(final Group group, final User user) {
|
||||
@ -117,8 +106,45 @@ public class GroupService {
|
||||
group.touch();
|
||||
user.touch();
|
||||
log.info("User left Group: user={}, group={}", user, group);
|
||||
groupMapper.publish(group);
|
||||
userPublicMapper.publish(user);
|
||||
publish(group);
|
||||
}
|
||||
|
||||
/* DTO ------------------------------------------------------------------------------------------ */
|
||||
|
||||
@NonNull
|
||||
public GroupDto toDto(@NonNull final Group group) {
|
||||
final UserPublicDto owner = new UserPublicDto(group.getOwner());
|
||||
final Set<UserPublicDto> users = group.getUsers().stream().map(UserPublicDto::new).collect(Collectors.toSet());
|
||||
return new GroupDto(group, owner, users);
|
||||
}
|
||||
|
||||
/* PUBLISH -------------------------------------------------------------------------------------- */
|
||||
|
||||
@NonNull
|
||||
public GroupDto publish(@NonNull final Group group) {
|
||||
final GroupDto dto = toDto(group);
|
||||
group.getUsers().forEach(user -> userPushService.push(user, dto));
|
||||
return dto;
|
||||
}
|
||||
|
||||
/* FIND & GET ----------------------------------------------------------------------------------- */
|
||||
|
||||
@NonNull
|
||||
public Set<GroupDto> findAllCommonGroups(@NonNull final User a, @NonNull final User b) {
|
||||
return groupRepository.findAllByUsersContainsAndUsersContains(a, b).stream().map(this::toDto).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Set<GroupDto> findAllJoined(@NonNull final String privateUuid) {
|
||||
final User principal = userService.access(privateUuid);
|
||||
return groupRepository.findAllByUsersContains(principal).stream().map(this::toDto).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Set<GroupDto> findAllCommon(@NonNull final String privateUuid, @NonNull final String targetUuid) {
|
||||
final User principal = userService.access(privateUuid);
|
||||
final User target = userService.getByPublicUuid(targetUuid);
|
||||
return findAllCommonGroups(principal, target);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,12 +1,16 @@
|
||||
package de.ph87.tools.tools.numbers;
|
||||
|
||||
import de.ph87.tools.group.Group;
|
||||
import de.ph87.tools.user.User;
|
||||
import de.ph87.tools.user.reference.UserReference;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@ -22,15 +26,20 @@ public class Numbers {
|
||||
private String uuid = UUID.randomUUID().toString();
|
||||
|
||||
@NonNull
|
||||
@Column(nullable = false)
|
||||
private String name = "Ohne Namen";
|
||||
|
||||
@NonNull
|
||||
@OneToOne
|
||||
@ManyToOne(optional = false)
|
||||
private Group group;
|
||||
|
||||
public Numbers(@NonNull final Group group) {
|
||||
@NonNull
|
||||
@Column(nullable = false)
|
||||
private ZonedDateTime date = ZonedDateTime.now();
|
||||
|
||||
@NonNull
|
||||
@OneToMany(orphanRemoval = true)
|
||||
private List<UserReference> users;
|
||||
|
||||
public Numbers(@NonNull final Group group, @NonNull final List<UserReference> users) {
|
||||
this.group = group;
|
||||
this.users = users;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -46,4 +55,18 @@ public class Numbers {
|
||||
return uuid.hashCode();
|
||||
}
|
||||
|
||||
public int getNumberForUser(@NonNull final User user) {
|
||||
for (int userReferenceIndex = 0; userReferenceIndex < users.size(); userReferenceIndex++) {
|
||||
final UserReference userReference = users.get(userReferenceIndex);
|
||||
if (userReference.getUser() != null && userReference.getUser().equals(user)) {
|
||||
return userReferenceIndex;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
public boolean containsUser(@NonNull final User user) {
|
||||
return users.stream().anyMatch(u -> user.equalsIUser(u.getUser()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,14 +1,41 @@
|
||||
package de.ph87.tools.tools.numbers;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import static de.ph87.tools.UserArgumentResolver.USER_UUID_COOKIE_NAME;
|
||||
|
||||
@Slf4j
|
||||
@CrossOrigin
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("Numbers")
|
||||
public class NumbersController {
|
||||
|
||||
private final NumbersService numbersService;
|
||||
|
||||
@GetMapping("create/{groupUuid}")
|
||||
public void create(@CookieValue(name = USER_UUID_COOKIE_NAME) @NonNull final String privateUuid, @PathVariable final String groupUuid) {
|
||||
numbersService.create(privateUuid, groupUuid);
|
||||
}
|
||||
|
||||
@PostMapping("canAccess")
|
||||
public boolean canAccess(@CookieValue(name = USER_UUID_COOKIE_NAME) @NonNull final String privateUuid, @RequestBody final String groupUuid) {
|
||||
return numbersService.canAccess(privateUuid, groupUuid);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@PostMapping("getGroupUuid")
|
||||
public String getGroupUuid(@RequestBody final String groupUuid) {
|
||||
return numbersService.getGroupUuid(groupUuid);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@PostMapping("byUuid")
|
||||
public NumbersPrivateDto byUuid(@CookieValue(name = USER_UUID_COOKIE_NAME) @NonNull final String privateUuid, @RequestBody final String groupUuid) {
|
||||
return numbersService.dtoByUuid(privateUuid, groupUuid);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -5,23 +5,28 @@ import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
@Getter
|
||||
@ToString(callSuper = true)
|
||||
public class NumbersDto {
|
||||
public class NumbersPrivateDto {
|
||||
|
||||
@NonNull
|
||||
private final String uuid;
|
||||
|
||||
@NonNull
|
||||
private final String name;
|
||||
|
||||
@NonNull
|
||||
private final GroupDto group;
|
||||
|
||||
public NumbersDto(@NonNull final Numbers numbers, @NonNull final GroupDto group) {
|
||||
@NonNull
|
||||
private final ZonedDateTime date;
|
||||
|
||||
private final int number;
|
||||
|
||||
public NumbersPrivateDto(@NonNull final Numbers numbers, @NonNull final GroupDto group, final int number) {
|
||||
this.uuid = numbers.getUuid();
|
||||
this.name = numbers.getName();
|
||||
this.date = numbers.getDate();
|
||||
this.group = group;
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package de.ph87.tools.tools.numbers;
|
||||
|
||||
import org.springframework.data.repository.ListCrudRepository;
|
||||
|
||||
public interface NumbersRepository extends ListCrudRepository<Numbers, String> {
|
||||
|
||||
}
|
||||
116
src/main/java/de/ph87/tools/tools/numbers/NumbersService.java
Normal file
116
src/main/java/de/ph87/tools/tools/numbers/NumbersService.java
Normal file
@ -0,0 +1,116 @@
|
||||
package de.ph87.tools.tools.numbers;
|
||||
|
||||
import de.ph87.tools.group.GroupAccessService;
|
||||
import de.ph87.tools.group.GroupDto;
|
||||
import de.ph87.tools.group.GroupService;
|
||||
import de.ph87.tools.user.User;
|
||||
import de.ph87.tools.user.UserService;
|
||||
import de.ph87.tools.user.push.UserPushService;
|
||||
import de.ph87.tools.user.reference.UserReference;
|
||||
import de.ph87.tools.user.reference.UserReferenceService;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class NumbersService {
|
||||
|
||||
private final NumbersRepository numbersRepository;
|
||||
|
||||
private final GroupAccessService groupAccessService;
|
||||
|
||||
private final UserReferenceService userReferenceService;
|
||||
|
||||
private final GroupService groupService;
|
||||
|
||||
private final UserPushService userPushService;
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
public void create(@NonNull final String userPrivateUuid, @NonNull final String groupUuid) {
|
||||
final de.ph87.tools.group.Access access = groupAccessService.accessAsOwner(userPrivateUuid, groupUuid);
|
||||
final List<UserReference> users = access.getGroup().getUsers().stream().map(userReferenceService::create).collect(Collectors.toList());
|
||||
Collections.shuffle(users);
|
||||
final Numbers numbers = numbersRepository.save(new Numbers(access.getGroup(), users));
|
||||
publish(numbers);
|
||||
}
|
||||
|
||||
private void publish(@NonNull final Numbers numbers) {
|
||||
numbers.getUsers()
|
||||
.stream()
|
||||
.map(UserReference::getUser)
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(user -> publish(numbers, user));
|
||||
}
|
||||
|
||||
private void publish(@NonNull final Numbers numbers, @NonNull final User user) {
|
||||
final NumbersPrivateDto dto = toPrivateDto(numbers, user);
|
||||
log.debug("Sending event: {}", dto);
|
||||
userPushService.push(user, dto);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public NumbersPrivateDto toPrivateDto(@NonNull final Numbers numbers, @NonNull final User user) {
|
||||
final GroupDto group = groupService.toDto(numbers.getGroup());
|
||||
final int number = numbers.getNumberForUser(user);
|
||||
return new NumbersPrivateDto(numbers, group, number);
|
||||
}
|
||||
|
||||
public boolean canAccess(@NonNull final String privateUuid, @NonNull final String numbersUuid) {
|
||||
final User user = userService.access(privateUuid);
|
||||
final Numbers numbers = numbersRepository.findById(numbersUuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
|
||||
return numbers.containsUser(user);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getGroupUuid(@NonNull final String numbersUuid) {
|
||||
final Numbers numbers = numbersRepository.findById(numbersUuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
|
||||
return numbers.getGroup().getUuid();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public NumbersPrivateDto dtoByUuid(@NonNull final String privateUuid, @NonNull final String numbersUuid) {
|
||||
final Access access = access(privateUuid, numbersUuid);
|
||||
return toPrivateDto(access.numbers, access.user);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Access access(@NonNull final String privateUuid, @NonNull final String numbersUuid) {
|
||||
final User user = userService.access(privateUuid);
|
||||
final Numbers numbers = numbersRepository.findById(numbersUuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
|
||||
if (!numbers.containsUser(user)) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
return new Access(numbers, user);
|
||||
}
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
private static class Access {
|
||||
|
||||
public final Numbers numbers;
|
||||
|
||||
public final User user;
|
||||
|
||||
public Access(final Numbers numbers, final User user) {
|
||||
this.numbers = numbers;
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
package de.ph87.tools.user;
|
||||
|
||||
import de.ph87.tools.web.IWebSocketMessage;
|
||||
import de.ph87.tools.user.reference.IUser;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
@ -9,7 +9,6 @@ import lombok.*;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@ -17,7 +16,7 @@ import java.util.UUID;
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@Table(name = "`user`")
|
||||
public class User implements IWebSocketMessage {
|
||||
public class User extends IUser {
|
||||
|
||||
@Id
|
||||
@NonNull
|
||||
@ -56,24 +55,6 @@ public class User implements IWebSocketMessage {
|
||||
lastAccess = ZonedDateTime.now();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> getWebsocketTopic() {
|
||||
return List.of("User", privateUuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (!(obj instanceof final User user)) {
|
||||
return false;
|
||||
}
|
||||
return user.privateUuid.equals(this.privateUuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return privateUuid.hashCode();
|
||||
}
|
||||
|
||||
public void setPassword(@NonNull final PasswordEncoder passwordEncoder, @NonNull final String password) {
|
||||
this.password = passwordEncoder.encode(password);
|
||||
}
|
||||
|
||||
@ -21,7 +21,13 @@ public class UserController {
|
||||
@Nullable
|
||||
@GetMapping("whoAmI")
|
||||
public UserPrivateDto whoAmI(@CookieValue(name = USER_UUID_COOKIE_NAME, required = false) @Nullable final String userUuid, @NonNull final HttpServletResponse response) {
|
||||
return userService.getUserByPrivateUuidOrNull(userUuid, response);
|
||||
return userService.whoAmI(userUuid, response);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@PostMapping("getByPublicUuid")
|
||||
public UserPublicDto getByPublicUuid(@NonNull @RequestBody final String publicUuid) {
|
||||
return userService.getDtoByPublicUuid(publicUuid);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@ -42,12 +48,6 @@ public class UserController {
|
||||
return userService.changePassword(userUuid, password);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@PostMapping("getCommonByUuid")
|
||||
public UserCommonDto getCommonByUuid(@CookieValue(name = USER_UUID_COOKIE_NAME) @NonNull final String userUuid, @NonNull @RequestBody final String targetUuid) {
|
||||
return userService.getCommonByUuid(userUuid, targetUuid);
|
||||
}
|
||||
|
||||
@GetMapping("delete")
|
||||
public void delete(@CookieValue(name = USER_UUID_COOKIE_NAME) @NonNull final String userUuid, @NonNull final HttpServletResponse response) {
|
||||
userService.delete(userUuid, response);
|
||||
|
||||
@ -1,18 +1,19 @@
|
||||
package de.ph87.tools.user;
|
||||
|
||||
import de.ph87.tools.group.GroupDto;
|
||||
import de.ph87.tools.web.IWebSocketMessage;
|
||||
import de.ph87.tools.user.reference.IUser;
|
||||
import jakarta.annotation.Nullable;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
public class UserPrivateDto implements IWebSocketMessage {
|
||||
public class UserPrivateDto extends IUser {
|
||||
|
||||
@NonNull
|
||||
private final String pushType = "UserPrivate";
|
||||
|
||||
@NonNull
|
||||
public final String publicUuid;
|
||||
@ -26,23 +27,22 @@ public class UserPrivateDto implements IWebSocketMessage {
|
||||
@NonNull
|
||||
public final ZonedDateTime created;
|
||||
|
||||
@NonNull
|
||||
private final Set<GroupDto> groups;
|
||||
|
||||
private final boolean password;
|
||||
|
||||
public UserPrivateDto(@NonNull final User user, final @NonNull Set<GroupDto> groups) {
|
||||
public UserPrivateDto(@NonNull final User user) {
|
||||
this.publicUuid = user.getPublicUuid();
|
||||
this.name = user.getName();
|
||||
this.privateUuid = user.getPrivateUuid();
|
||||
this.created = user.getCreated();
|
||||
this.password = !user.getPassword().isEmpty();
|
||||
this.groups = groups;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> getWebsocketTopic() {
|
||||
return List.of("User", privateUuid);
|
||||
@Nullable
|
||||
public static UserPrivateDto orNull(@Nullable final User user) {
|
||||
if (user == null) {
|
||||
return null;
|
||||
}
|
||||
return new UserPrivateDto(user);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,48 +0,0 @@
|
||||
package de.ph87.tools.user;
|
||||
|
||||
import de.ph87.tools.group.GroupDto;
|
||||
import de.ph87.tools.group.GroupMapper;
|
||||
import jakarta.annotation.Nullable;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@Transactional
|
||||
@EnableScheduling
|
||||
@RequiredArgsConstructor
|
||||
public class UserPrivateMapper {
|
||||
|
||||
private final GroupMapper groupMapper;
|
||||
|
||||
private final ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
@NonNull
|
||||
public UserPrivateDto toPrivateDto(@NonNull final User user) {
|
||||
final Set<GroupDto> groups = groupMapper.findAllByUser(user);
|
||||
return new UserPrivateDto(user, groups);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public UserPrivateDto toPrivateDtoOrNull(@Nullable final User user) {
|
||||
if (user == null) {
|
||||
return null;
|
||||
}
|
||||
return toPrivateDto(user);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public UserPrivateDto publish(@NonNull final User user) {
|
||||
final UserPrivateDto dto = toPrivateDto(user);
|
||||
applicationEventPublisher.publishEvent(dto);
|
||||
return dto;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,15 +1,13 @@
|
||||
package de.ph87.tools.user;
|
||||
|
||||
import de.ph87.tools.web.IWebSocketMessage;
|
||||
import de.ph87.tools.user.reference.IUser;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
public class UserPublicDto implements IWebSocketMessage {
|
||||
public class UserPublicDto extends IUser {
|
||||
|
||||
@NonNull
|
||||
public final String publicUuid;
|
||||
@ -22,9 +20,4 @@ public class UserPublicDto implements IWebSocketMessage {
|
||||
this.name = user.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> getWebsocketTopic() {
|
||||
return List.of("User", publicUuid);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
package de.ph87.tools.user;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@Transactional
|
||||
@EnableScheduling
|
||||
@RequiredArgsConstructor
|
||||
public class UserPublicMapper {
|
||||
|
||||
private final ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
@NonNull
|
||||
public UserPublicDto toPublicDto(@NonNull final User user) {
|
||||
return new UserPublicDto(user);
|
||||
}
|
||||
|
||||
public void publish(@NonNull final User user) {
|
||||
final UserPublicDto dto = toPublicDto(user);
|
||||
applicationEventPublisher.publishEvent(dto);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,7 +1,5 @@
|
||||
package de.ph87.tools.user;
|
||||
|
||||
import de.ph87.tools.group.GroupDto;
|
||||
import de.ph87.tools.group.GroupMapper;
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
@ -17,7 +15,6 @@ import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -42,12 +39,6 @@ public class UserService {
|
||||
|
||||
private final UserRepository userRepository;
|
||||
|
||||
private final UserPrivateMapper userPrivateMapper;
|
||||
|
||||
private final UserPublicMapper userPublicMapper;
|
||||
|
||||
private final GroupMapper groupMapper;
|
||||
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
@NonNull
|
||||
@ -58,8 +49,7 @@ public class UserService {
|
||||
}
|
||||
user.touch();
|
||||
writeUserUuidCookie(response, user);
|
||||
userPublicMapper.publish(user);
|
||||
return userPrivateMapper.publish(user);
|
||||
return new UserPrivateDto(user);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@ -70,13 +60,13 @@ public class UserService {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public UserPrivateDto getUserByPrivateUuidOrNull(@Nullable final String privateUuid, final @NonNull HttpServletResponse response) {
|
||||
public UserPrivateDto whoAmI(@Nullable final String privateUuid, final @NonNull HttpServletResponse response) {
|
||||
if (privateUuid == null || privateUuid.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final User user = userRepository.findByPrivateUuid(privateUuid).orElse(null);
|
||||
writeUserUuidCookie(response, user);
|
||||
return userPrivateMapper.toPrivateDtoOrNull(user);
|
||||
return UserPrivateDto.orNull(user);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@ -119,14 +109,6 @@ public class UserService {
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public UserCommonDto getCommonByUuid(@NonNull final String privateUuid, @NonNull final String targetUuid) {
|
||||
final User principal = getByPrivateUuidOrThrow(privateUuid);
|
||||
final User target = getByPublicUuid(targetUuid);
|
||||
final Set<GroupDto> commonGroups = groupMapper.findAllCommonGroups(principal, target);
|
||||
return new UserCommonDto(target, commonGroups);
|
||||
}
|
||||
|
||||
public void delete(@NonNull final String privateUuid, final @NonNull HttpServletResponse response) {
|
||||
final User user = userRepository.findByPrivateUuid(privateUuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
|
||||
deleteUnchecked(response, user);
|
||||
@ -145,7 +127,7 @@ public class UserService {
|
||||
@NonNull
|
||||
private String generateFreeUserName() {
|
||||
int index = new Random().nextInt(1234, 5723);
|
||||
String name = USER_NAME_DEFAULT_BASE;
|
||||
String name = "%s #%d".formatted(USER_NAME_DEFAULT_BASE, index);
|
||||
while (userRepository.existsByName(name)) {
|
||||
index++;
|
||||
name = "%s #%d".formatted(USER_NAME_DEFAULT_BASE, index);
|
||||
@ -155,11 +137,9 @@ public class UserService {
|
||||
|
||||
@NonNull
|
||||
private UserPrivateDto modify(@NonNull final String privateUuid, @NonNull Consumer<User> modifier) {
|
||||
final User user = getByPrivateUuidOrThrow(privateUuid);
|
||||
final User user = access(privateUuid);
|
||||
modifier.accept(user);
|
||||
userPublicMapper.publish(user);
|
||||
userPrivateMapper.publish(user);
|
||||
return userPrivateMapper.toPrivateDto(user);
|
||||
return new UserPrivateDto(user);
|
||||
}
|
||||
|
||||
private void deleteUnchecked(final HttpServletResponse response, final User user) {
|
||||
@ -168,10 +148,25 @@ public class UserService {
|
||||
writeUserUuidCookie(response, null);
|
||||
}
|
||||
|
||||
/* ACCESS --------------------------------------------------------------------------------------- */
|
||||
|
||||
@Nullable
|
||||
public User accessOrNull(@Nullable final String userPrivateUuid) {
|
||||
if (userPrivateUuid == null || userPrivateUuid.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return access(userPrivateUuid);
|
||||
}
|
||||
|
||||
/* GETTERS & FINDERS ---------------------------------------------------------------------------- */
|
||||
|
||||
@NonNull
|
||||
public User getByPrivateUuidOrThrow(@NonNull final String privateUuid) {
|
||||
public UserPublicDto getDtoByPublicUuid(@NonNull final String publicUuid) {
|
||||
return new UserPublicDto(getByPublicUuid(publicUuid));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public User access(@NonNull final String privateUuid) {
|
||||
return userRepository.findByPrivateUuid(privateUuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
|
||||
}
|
||||
|
||||
@ -180,11 +175,6 @@ public class UserService {
|
||||
return userRepository.findByPublicUuid(publicUuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Optional<User> findByPrivateUuid(@NonNull final String privateUuid) {
|
||||
return userRepository.findByPrivateUuid(privateUuid);
|
||||
}
|
||||
|
||||
/* COOKIES -------------------------------------------------------------------------------------- */
|
||||
|
||||
private static void writeUserUuidCookie(@NonNull final HttpServletResponse response, @Nullable final User user) {
|
||||
|
||||
22
src/main/java/de/ph87/tools/user/push/UserPushMessage.java
Normal file
22
src/main/java/de/ph87/tools/user/push/UserPushMessage.java
Normal file
@ -0,0 +1,22 @@
|
||||
package de.ph87.tools.user.push;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
public class UserPushMessage {
|
||||
|
||||
@NonNull
|
||||
private final String _type_;
|
||||
|
||||
@NonNull
|
||||
private final Object payload;
|
||||
|
||||
public UserPushMessage(@NonNull final Object payload) {
|
||||
this._type_ = payload.getClass().getSimpleName();
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
}
|
||||
25
src/main/java/de/ph87/tools/user/push/UserPushService.java
Normal file
25
src/main/java/de/ph87/tools/user/push/UserPushService.java
Normal file
@ -0,0 +1,25 @@
|
||||
package de.ph87.tools.user.push;
|
||||
|
||||
import de.ph87.tools.user.User;
|
||||
import de.ph87.tools.web.WebSocketService;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UserPushService {
|
||||
|
||||
private final WebSocketService webSocketService;
|
||||
|
||||
public void push(@NonNull final User user, @NonNull final Object payload) {
|
||||
final List<Object> topic = List.of("User", user.getPrivateUuid());
|
||||
final UserPushMessage message = new UserPushMessage(payload);
|
||||
webSocketService.send(topic, message);
|
||||
}
|
||||
|
||||
}
|
||||
31
src/main/java/de/ph87/tools/user/reference/IUser.java
Normal file
31
src/main/java/de/ph87/tools/user/reference/IUser.java
Normal file
@ -0,0 +1,31 @@
|
||||
package de.ph87.tools.user.reference;
|
||||
|
||||
import jakarta.annotation.Nullable;
|
||||
import lombok.NonNull;
|
||||
|
||||
public abstract class IUser {
|
||||
|
||||
@NonNull
|
||||
public abstract String getPublicUuid();
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (!(obj instanceof final IUser user)) {
|
||||
return false;
|
||||
}
|
||||
return equalsIUser(user);
|
||||
}
|
||||
|
||||
public boolean equalsIUser(@Nullable final IUser user) {
|
||||
if (user == null) {
|
||||
return false;
|
||||
}
|
||||
return user.getPublicUuid().equals(this.getPublicUuid());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getPublicUuid().hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package de.ph87.tools.user.reference;
|
||||
|
||||
import de.ph87.tools.user.User;
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
|
||||
@Entity
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@Table(name = "`user_reference`")
|
||||
public class UserReference {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private long id;
|
||||
|
||||
@Nullable
|
||||
@ManyToOne
|
||||
private User user;
|
||||
|
||||
@NonNull
|
||||
@Column(nullable = false)
|
||||
private String name;
|
||||
|
||||
public UserReference(@NonNull final User user) {
|
||||
this.user = user;
|
||||
this.name = user.getName();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package de.ph87.tools.user.reference;
|
||||
|
||||
import org.springframework.data.repository.ListCrudRepository;
|
||||
|
||||
public interface UserReferenceRepository extends ListCrudRepository<UserReference, Long> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package de.ph87.tools.user.reference;
|
||||
|
||||
import de.ph87.tools.user.User;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class UserReferenceService {
|
||||
|
||||
private final UserReferenceRepository userReferenceRepository;
|
||||
|
||||
@NonNull
|
||||
public UserReference create(@NonNull final User user) {
|
||||
return userReferenceRepository.save(new UserReference(user));
|
||||
}
|
||||
|
||||
}
|
||||
@ -7,6 +7,7 @@ import org.springframework.lang.NonNull;
|
||||
import org.springframework.messaging.simp.SimpMessageSendingOperations;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@ -18,8 +19,12 @@ public class WebSocketService {
|
||||
|
||||
@EventListener(IWebSocketMessage.class)
|
||||
public void send(@NonNull final IWebSocketMessage message) {
|
||||
final String topic = message.getWebsocketTopic().stream().map(Object::toString).collect(Collectors.joining("/"));
|
||||
simpMessageSendingOperations.convertAndSend(topic, message);
|
||||
send(message.getWebsocketTopic(), message);
|
||||
}
|
||||
|
||||
public void send(@NonNull final List<Object> topic, @NonNull final Object message) {
|
||||
final String topicString = topic.stream().map(Object::toString).collect(Collectors.joining("/"));
|
||||
simpMessageSendingOperations.convertAndSend(topicString, message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user