Fixed user last login query
- Added frontend logic for first login - Added table index for user_logins table - Updated statistics rest endpoint
This commit is contained in:
parent
348bf8050c
commit
9ebbcc00ea
@ -38,7 +38,8 @@
|
|||||||
<div class="shadow stats bg-white">
|
<div class="shadow stats bg-white">
|
||||||
<div class="stat">
|
<div class="stat">
|
||||||
<div class="stat-title">Last Admin Login</div>
|
<div class="stat-title">Last Admin Login</div>
|
||||||
<div class="stat-value">{{ statistics.lastUserLogin | relativeTime }}</div>
|
<div *ngIf="statistics.lastUserLogin" class="stat-value">{{ statistics.lastUserLogin.loginTime | relativeTime }}</div>
|
||||||
|
<div *ngIf="!statistics.lastUserLogin" class="stat-value">First login...</div>
|
||||||
<div class="stat-desc">Most recent login</div>
|
<div class="stat-desc">Most recent login</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {Component, ElementRef, ViewChild} from '@angular/core';
|
import {Component, ElementRef, ViewChild} from '@angular/core';
|
||||||
import {DatePipe, DecimalPipe, NgForOf} from "@angular/common";
|
import {DatePipe, DecimalPipe, NgForOf, NgIf} from "@angular/common";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import {firstValueFrom} from "rxjs";
|
import {firstValueFrom} from "rxjs";
|
||||||
import {DevelopmentStore} from "../../store/DevelopmentStore";
|
import {DevelopmentStore} from "../../store/DevelopmentStore";
|
||||||
@ -22,7 +22,8 @@ import {EdituserComponent} from "./edituser/edituser.component";
|
|||||||
DurationPipe,
|
DurationPipe,
|
||||||
RelativeTimePipe,
|
RelativeTimePipe,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
EdituserComponent
|
EdituserComponent,
|
||||||
|
NgIf
|
||||||
],
|
],
|
||||||
templateUrl: './adminui.component.html',
|
templateUrl: './adminui.component.html',
|
||||||
styleUrl: './adminui.component.scss'
|
styleUrl: './adminui.component.scss'
|
||||||
@ -35,7 +36,7 @@ export class AdminuiComponent {
|
|||||||
expiredFileUploads: any[] = [];
|
expiredFileUploads: any[] = [];
|
||||||
totalFileSizeOnDisk: number = 0;
|
totalFileSizeOnDisk: number = 0;
|
||||||
totalFileDownloads = 0;
|
totalFileDownloads = 0;
|
||||||
statistics: any = {};
|
statistics: any = "";
|
||||||
username: string = "";
|
username: string = "";
|
||||||
|
|
||||||
constructor(private developmentStore: DevelopmentStore, private authStore: AuthStore, private router: Router) {
|
constructor(private developmentStore: DevelopmentStore, private authStore: AuthStore, private router: Router) {
|
||||||
|
@ -16,6 +16,8 @@ public class MvcConfig implements WebMvcConfigurer {
|
|||||||
registry.addViewController("/upload").setViewName("forward:/index.html");
|
registry.addViewController("/upload").setViewName("forward:/index.html");
|
||||||
registry.addViewController("/credits").setViewName("forward:/index.html");
|
registry.addViewController("/credits").setViewName("forward:/index.html");
|
||||||
registry.addViewController("/licenses").setViewName("forward:/index.html");
|
registry.addViewController("/licenses").setViewName("forward:/index.html");
|
||||||
|
registry.addViewController("/login").setViewName("forward:/index.html");
|
||||||
|
registry.addViewController("/secure/administration").setViewName("forward:/index.html");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@ -116,6 +117,15 @@ public class RethinkDBService {
|
|||||||
r.db(config.getDatabase()).table("user_logins").delete().run(connection);
|
r.db(config.getDatabase()).table("user_logins").delete().run(connection);
|
||||||
log.debug("Table 'user_logins' cleared successfully.");
|
log.debug("Table 'user_logins' cleared successfully.");
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
r.db(config.getDatabase()).table("user_logins").indexCreate("loginTime").run(connection);
|
||||||
|
log.debug("Secondary index 'loginTime' on table 'user_logins' successfully created.");
|
||||||
|
} catch (ReqlOpFailedError e) {
|
||||||
|
log.debug("Secondary index 'loginTime' already exists.");
|
||||||
|
} finally {
|
||||||
|
r.db(config.getDatabase()).table("user_logins").indexWait("loginTime").run(connection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeAdminUser();
|
initializeAdminUser();
|
||||||
|
@ -1,28 +1,17 @@
|
|||||||
package de.w665.sharepulse.db.repo;
|
package de.w665.sharepulse.db.repo;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.reflect.TypeToken;
|
|
||||||
import com.rethinkdb.RethinkDB;
|
import com.rethinkdb.RethinkDB;
|
||||||
import com.rethinkdb.net.Connection;
|
import com.rethinkdb.net.Connection;
|
||||||
import com.rethinkdb.net.Result;
|
import com.rethinkdb.net.Result;
|
||||||
import de.w665.sharepulse.db.RethinkDBConfig;
|
import de.w665.sharepulse.db.RethinkDBConfig;
|
||||||
import de.w665.sharepulse.db.RethinkDBConnector;
|
import de.w665.sharepulse.db.RethinkDBConnector;
|
||||||
import de.w665.sharepulse.model.FileUpload;
|
|
||||||
import de.w665.sharepulse.model.User;
|
|
||||||
import de.w665.sharepulse.model.UserLogin;
|
import de.w665.sharepulse.model.UserLogin;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Repository
|
@Repository
|
||||||
public class UserLoginRepository {
|
public class UserLoginRepository {
|
||||||
@ -48,34 +37,14 @@ public class UserLoginRepository {
|
|||||||
r.db(config.getDatabase()).table(TABLE_NAME).insert(userLogin).run(connection);
|
r.db(config.getDatabase()).table(TABLE_NAME).insert(userLogin).run(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<UserLogin> getRecentUserLoginsForUser(String userId, int limit) throws JsonProcessingException {
|
public UserLogin getLastLogin(String userId) {
|
||||||
Result<Object> rawResult = r.db("sharepulse").table(TABLE_NAME)
|
// Get the second most recent login (the most recent is the current one)
|
||||||
.filter(r.hashMap("userId", userId))
|
Result<UserLogin> result = r.db(config.getDatabase()).table(TABLE_NAME)
|
||||||
.orderBy(r.desc("sort"))
|
.orderBy().optArg("index", r.desc("loginTime"))
|
||||||
.limit(limit)
|
|
||||||
.run(connection);
|
|
||||||
|
|
||||||
String jsonString = mapper.writeValueAsString(rawResult.single());
|
|
||||||
Type listType = new TypeToken<List<UserLogin>>() {}.getType();
|
|
||||||
return gson.fromJson(jsonString, listType);
|
|
||||||
}
|
|
||||||
|
|
||||||
private UserLogin convertToUserLogin(Object obj) {
|
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
|
||||||
return mapper.convertValue(obj, UserLogin.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*public List<UserLogin> getRecentUserLoginsForUser(String userId, int limit) {
|
|
||||||
Result<Object> results = r.db(config.getDatabase()).table(TABLE_NAME)
|
|
||||||
.filter(r.hashMap("userId", userId))
|
.filter(r.hashMap("userId", userId))
|
||||||
.orderBy(r.desc("loginTime"))
|
.skip(1).limit(1)
|
||||||
.limit(limit)
|
.run(connection, UserLogin.class);
|
||||||
.run(connection);
|
// Return the second most recent login if exists
|
||||||
List<Object> objectList = results.toList();
|
return result.hasNext() ? result.next() : null;
|
||||||
List<Object> obj = (List) objectList.get(0);
|
}
|
||||||
System.out.println(obj.getFirst());
|
|
||||||
UserLogin userLogin = (UserLogin) obj.getFirst();
|
|
||||||
System.out.println(userLogin.toString());
|
|
||||||
return null;
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
@ -18,5 +18,4 @@ public class UserLogin {
|
|||||||
@JsonFormat(timezone = "ETC")
|
@JsonFormat(timezone = "ETC")
|
||||||
Date loginTime;
|
Date loginTime;
|
||||||
String loginIp;
|
String loginIp;
|
||||||
int sort;
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package de.w665.sharepulse.rest.mappings;
|
package de.w665.sharepulse.rest.mappings;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import de.w665.sharepulse.SharepulseApplication;
|
import de.w665.sharepulse.SharepulseApplication;
|
||||||
import de.w665.sharepulse.db.repo.UserLoginRepository;
|
import de.w665.sharepulse.db.repo.UserLoginRepository;
|
||||||
import de.w665.sharepulse.db.repo.UserRepository;
|
import de.w665.sharepulse.db.repo.UserRepository;
|
||||||
@ -38,7 +37,6 @@ public class Administration extends SecureApiRestController {
|
|||||||
|
|
||||||
@GetMapping("/statistics")
|
@GetMapping("/statistics")
|
||||||
public ResponseEntity<Object> getStatistics(HttpServletRequest request) {
|
public ResponseEntity<Object> getStatistics(HttpServletRequest request) {
|
||||||
|
|
||||||
// TODO: FIX LAST LOGIN
|
// TODO: FIX LAST LOGIN
|
||||||
|
|
||||||
String token = request.getHeader("Authorization");
|
String token = request.getHeader("Authorization");
|
||||||
@ -54,12 +52,7 @@ public class Administration extends SecureApiRestController {
|
|||||||
|
|
||||||
Map<String, Object> response = new HashMap<>();
|
Map<String, Object> response = new HashMap<>();
|
||||||
response.put("applicationOnlineTime", System.currentTimeMillis() - SharepulseApplication.startTime.getTime());
|
response.put("applicationOnlineTime", System.currentTimeMillis() - SharepulseApplication.startTime.getTime());
|
||||||
try {
|
response.put("lastUserLogin", userLoginRepository.getLastLogin(user.getId()));
|
||||||
response.put("lastUserLogin", userLoginRepository.getRecentUserLoginsForUser(user.getId(), 2));
|
|
||||||
} catch (JsonProcessingException e) {
|
|
||||||
response.put("lastUserLogin", "Error retrieving last user login");
|
|
||||||
}
|
|
||||||
log.debug("Received statistics request");
|
|
||||||
return ResponseEntity.ok(response);
|
return ResponseEntity.ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,9 +20,6 @@ import java.util.Optional;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class AuthenticationService {
|
public class AuthenticationService {
|
||||||
|
|
||||||
static int count = 0;
|
|
||||||
|
|
||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
private final UserLoginRepository userLoginRepository;
|
private final UserLoginRepository userLoginRepository;
|
||||||
|
|
||||||
@ -54,7 +51,7 @@ public class AuthenticationService {
|
|||||||
if (optionalUser.isPresent() && passwordEncoder.matches(password, optionalUser.get().getPassword())) {
|
if (optionalUser.isPresent() && passwordEncoder.matches(password, optionalUser.get().getPassword())) {
|
||||||
User user = optionalUser.get();
|
User user = optionalUser.get();
|
||||||
|
|
||||||
userLoginRepository.insertUserLogin(new UserLogin("", user.getId(), new Date(), remoteAddr, count++));
|
userLoginRepository.insertUserLogin(new UserLogin(""/*Auto generated*/, user.getId(), new Date(), remoteAddr));
|
||||||
userRepository.updateLastLoginForUser(user.getUsername(), new Date());
|
userRepository.updateLastLoginForUser(user.getUsername(), new Date());
|
||||||
return generateToken(user);
|
return generateToken(user);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user