Added loginhistory view

- Added tooltip to keep logged in
- Added Administration button to credits UI
This commit is contained in:
Max W. 2024-06-03 23:47:48 +02:00
parent c069028bd5
commit 1e464f6fb8
7 changed files with 77 additions and 8 deletions

View File

@ -1,4 +1,4 @@
<div class="container mx-auto p-4 mt-4">
<div class="container mx-auto p-4 mt-4 animate-in fade-in slide-in-from-bottom duration-500">
<h1 class="text-5xl font-bold text-center text-gray-800 mb-10">Admin Dashboard</h1>
<!-- Statistics Section -->
@ -54,11 +54,13 @@
Change Administrator Login
</button>
<button class="btn btn-secondary" (click)="confirm('Are you sure?') && deleteAllFileUploads()">Delete All Uploaded Files</button>
<button class="btn btn-accent">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-people" viewBox="0 0 16 16">
<path d="M15 14s1 0 1-1-1-4-5-4-5 3-5 4 1 1 1 1zm-7.978-1L7 12.996c.001-.264.167-1.03.76-1.72C8.312 10.629 9.282 10 11 10c1.717 0 2.687.63 3.24 1.276.593.69.758 1.457.76 1.72l-.008.002-.014.002zM11 7a2 2 0 1 0 0-4 2 2 0 0 0 0 4m3-2a3 3 0 1 1-6 0 3 3 0 0 1 6 0M6.936 9.28a6 6 0 0 0-1.23-.247A7 7 0 0 0 5 9c-4 0-5 3-5 4q0 1 1 1h4.216A2.24 2.24 0 0 1 5 13c0-1.01.377-2.042 1.09-2.904.243-.294.526-.569.846-.816M4.92 10A5.5 5.5 0 0 0 4 13H1c0-.26.164-1.03.76-1.724.545-.636 1.492-1.256 3.16-1.275ZM1.5 5.5a3 3 0 1 1 6 0 3 3 0 0 1-6 0m3-2a2 2 0 1 0 0 4 2 2 0 0 0 0-4"/>
<button class="btn btn-accent" (click)="openLoginHistoryModal()">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-clock-history" viewBox="0 0 16 16">
<path d="M8.515 1.019A7 7 0 0 0 8 1V0a8 8 0 0 1 .589.022zm2.004.45a7 7 0 0 0-.985-.299l.219-.976q.576.129 1.126.342zm1.37.71a7 7 0 0 0-.439-.27l.493-.87a8 8 0 0 1 .979.654l-.615.789a7 7 0 0 0-.418-.302zm1.834 1.79a7 7 0 0 0-.653-.796l.724-.69q.406.429.747.91zm.744 1.352a7 7 0 0 0-.214-.468l.893-.45a8 8 0 0 1 .45 1.088l-.95.313a7 7 0 0 0-.179-.483m.53 2.507a7 7 0 0 0-.1-1.025l.985-.17q.1.58.116 1.17zm-.131 1.538q.05-.254.081-.51l.993.123a8 8 0 0 1-.23 1.155l-.964-.267q.069-.247.12-.501m-.952 2.379q.276-.436.486-.908l.914.405q-.24.54-.555 1.038zm-.964 1.205q.183-.183.35-.378l.758.653a8 8 0 0 1-.401.432z"/>
<path d="M8 1a7 7 0 1 0 4.95 11.95l.707.707A8.001 8.001 0 1 1 8 0z"/>
<path d="M7.5 3a.5.5 0 0 1 .5.5v5.21l3.248 1.856a.5.5 0 0 1-.496.868l-3.5-2A.5.5 0 0 1 7 9V3.5a.5.5 0 0 1 .5-.5"/>
</svg>
User Management
Login History
</button>
<button class="btn btn-neutral" (click)="logout()">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-box-arrow-left" viewBox="0 0 16 16">
@ -153,3 +155,10 @@
></app-edituser>
</div>
</dialog>
<dialog #login_history_modal class="modal">
<div class="modal-box w-11/12 max-w-5xl">
<app-loginhistory>
[username]="username"
</app-loginhistory>
</div>
</dialog>

View File

@ -10,6 +10,7 @@ import {DurationPipe} from "../duration.pipe";
import {RelativeTimePipe} from "../relative-time.pipe";
import {FormsModule} from "@angular/forms";
import {EdituserComponent} from "./edituser/edituser.component";
import {LoginhistoryComponent} from "./loginhistory/loginhistory.component";
@Component({
selector: 'app-adminui',
@ -23,7 +24,8 @@ import {EdituserComponent} from "./edituser/edituser.component";
RelativeTimePipe,
FormsModule,
EdituserComponent,
NgIf
NgIf,
LoginhistoryComponent
],
templateUrl: './adminui.component.html',
styleUrl: './adminui.component.scss'
@ -31,6 +33,7 @@ import {EdituserComponent} from "./edituser/edituser.component";
export class AdminuiComponent {
@ViewChild('edit_user_modal') edit_user_modal: ElementRef<HTMLDialogElement> | undefined;
@ViewChild('login_history_modal') login_history_modal: ElementRef<HTMLDialogElement> | undefined;
fileUploads: any[] = [];
expiredFileUploads: any[] = [];
@ -83,6 +86,10 @@ export class AdminuiComponent {
this.edit_user_modal?.nativeElement.showModal();
}
openLoginHistoryModal() {
this.login_history_modal?.nativeElement.showModal();
}
logout() {
this.authStore.setToken("");
this.authStore.setUsername("");

View File

@ -1,3 +1,6 @@
<form method="dialog">
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"></button>
</form>
<div class="w-full">
<form>
<h3 class="font-bold text-lg mb-10">Edit your ({{parsedUsername}}) account details</h3>

View File

@ -42,9 +42,15 @@
</div>
</div>
<button class="btn mb-10" routerLink="/login">Administration</button>
<p>
<span class="text-indigo-600 cursor-pointer" (click)="openPrivacyPolicyModal()">Privacy Policy</span> |
<span class="text-indigo-600 cursor-pointer" (click)="openTermsOfUseModal()">Terms of Use</span>
</p>
<div>
<p class="text-gray-800 mt-10">© 2024 SharePulse. All rights reserved.</p>
</div>
</div>

View File

@ -17,10 +17,10 @@
<input class="input w-full shadow text-center" id="password" type="password" placeholder="********"
[(ngModel)]="inputPassword" name="password"
[ngClass]="{'input-error': loginFailed}"
(keydown.enter)="tryToLogin()" >
(keydown.enter)="tryToLogin()">
</div>
<div class="mb-5">
<label class="flex items-center justify-center">
<label class="flex items-center justify-center tooltip" data-tip="Storing login sessions is not functional yet">
<input type="checkbox" class="toggle" checked name="keepSignedIn"/>
<span class="ml-2 text-gray-700">Keep me signed in</span>
</label>

View File

@ -12,6 +12,8 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.List;
@Slf4j
@Repository
public class UserLoginRepository {
@ -47,4 +49,19 @@ public class UserLoginRepository {
// Return the second most recent login if exists
return result.hasNext() ? result.next() : null;
}
public List<UserLogin> getUserLogins(String userId) {
Result<UserLogin> result = r.db(config.getDatabase()).table(TABLE_NAME)
.orderBy().optArg("index", r.desc("loginTime"))
.filter(r.hashMap("userId", userId))
.run(connection, UserLogin.class);
return result.toList();
}
public void deleteAllUserLogins(String userId) {
r.db(config.getDatabase()).table(TABLE_NAME)
.filter(r.hashMap("userId", userId))
.delete()
.run(connection);
}
}

View File

@ -75,6 +75,33 @@ public class Administration extends SecureApiRestController {
return ResponseEntity.ok(user);
}
@GetMapping("/loginhistory")
public ResponseEntity<Object> getLoginHistory(HttpServletRequest request) {
String token = request.getHeader("Authorization");
token = token.substring(7);
String username = authenticationService.getClaimValue(token, "username", String.class);
Optional<User> optionalUser = userRepository.retrieveUserByUsername(username);
if(optionalUser.isEmpty()) {
return ResponseEntity.badRequest().body("User not found");
}
String userId = optionalUser.get().getId();
return ResponseEntity.ok(userLoginRepository.getUserLogins(userId));
}
@DeleteMapping("/loginhistory")
public ResponseEntity<Object> deleteLoginHistory(HttpServletRequest request) {
String token = request.getHeader("Authorization");
token = token.substring(7);
String username = authenticationService.getClaimValue(token, "username", String.class);
Optional<User> optionalUser = userRepository.retrieveUserByUsername(username);
if(optionalUser.isEmpty()) {
return ResponseEntity.badRequest().body("User not found");
}
String userId = optionalUser.get().getId();
userLoginRepository.deleteAllUserLogins(userId);
return ResponseEntity.ok("User logins deleted successfully!");
}
@DeleteMapping("/files")
public ResponseEntity<Object> deleteFiles(HttpServletRequest request) {
List<FileUpload> files = fileCleanupService.deleteFiles();