First commit
This commit is contained in:
28
.gitignore
vendored
Normal file
28
.gitignore
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
/target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/build/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
|
||||
.mvn
|
||||
**/node_modules
|
||||
84
pom.xml
Normal file
84
pom.xml
Normal file
@@ -0,0 +1,84 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.minager</groupId>
|
||||
<artifactId>minager</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>Minager</name>
|
||||
<description>Minecraft server managing application</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.0.RELEASE</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.mindrot/jbcrypt -->
|
||||
<dependency>
|
||||
<groupId>org.mindrot</groupId>
|
||||
<artifactId>jbcrypt</artifactId>
|
||||
<version>0.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<!-- <dependency> -->
|
||||
<!-- <groupId>commons-lang</groupId> -->
|
||||
<!-- <artifactId>commons-lang</artifactId> -->
|
||||
<!-- <version>2.3</version> -->
|
||||
<!-- </dependency> -->
|
||||
<!-- https://mvnrepository.com/artifact/commons-lang/commons-lang -->
|
||||
<!-- <dependency> -->
|
||||
<!-- <groupId>org.apache.commons</groupId> -->
|
||||
<!-- <artifactId>commons-lang3</artifactId> -->
|
||||
<!-- <version>3.7</version> -->
|
||||
<!-- </dependency> -->
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<executable>true</executable>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
||||
28
src/main/bash/minager-service
Normal file
28
src/main/bash/minager-service
Normal file
@@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
|
||||
USER=minecraft
|
||||
MINECRAFT_SERVER_PATH=/home/minecraft/minager/
|
||||
MINECRAFT_SHELL=minager.sh
|
||||
|
||||
case "$1" in
|
||||
'start')
|
||||
sudo -H -u $USER bash -c "$MINECRAFT_SERVER_PATH/$MINECRAFT_SHELL start"
|
||||
;;
|
||||
'stop')
|
||||
sudo -H -u $USER bash -c "$MINECRAFT_SERVER_PATH/$MINECRAFT_SHELL stop"
|
||||
;;
|
||||
'status')
|
||||
sudo -H -u $USER bash -c "$MINECRAFT_SERVER_PATH/$MINECRAFT_SHELL status"
|
||||
;;
|
||||
'restart')
|
||||
sudo -H -u $USER bash -c "$MINECRAFT_SERVER_PATH/$MINECRAFT_SHELL restart"
|
||||
;;
|
||||
*)
|
||||
# If no argument, we launch the app in case of server startup
|
||||
sudo -H -u $USER bash -c "$MINECRAFT_SERVER_PATH/$MINECRAFT_SHELL start &>/dev/null"
|
||||
# And show the script usage
|
||||
echo "Usage: /etc/init.d/minecraft {start|stop|status|restart}\n" >&2
|
||||
exit 3
|
||||
;;
|
||||
esac
|
||||
exit 0
|
||||
80
src/main/bash/minager.sh
Normal file
80
src/main/bash/minager.sh
Normal file
@@ -0,0 +1,80 @@
|
||||
#!/bin/sh
|
||||
# kFreeBSD do not accept scripts as interpreters, using #!/bin/sh and sourcing.
|
||||
if [ true != "$INIT_D_SCRIPT_SOURCED" ] ; then
|
||||
set "$0" "$@"; INIT_D_SCRIPT_SOURCED=true . /lib/init/init-d-script
|
||||
fi
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
NC='\033[0m'
|
||||
|
||||
DESC="The Minager web app server"
|
||||
NAME='minager'
|
||||
MINECRAFT_HOME='/home/minecraft'
|
||||
MINAGER_HOME="$MINECRAFT_HOME/minager"
|
||||
DAEMON_HOME="$MINAGER_HOME/bin"
|
||||
DAEMON="$DAEMON_HOME/$NAME.jar"
|
||||
PIDFILE="$DAEMON_HOME/$NAME.pid"
|
||||
SCRIPTNAME="$DAEMON_HOME/$0"
|
||||
|
||||
start()
|
||||
{
|
||||
# If PIDFILE exists and PID is in running processes
|
||||
if [ -f $PIDFILE ] && kill -0 `cat $PIDFILE` 2>/dev/null
|
||||
then
|
||||
echo 'Service already running\n'
|
||||
else
|
||||
echo "Starting service $NAME"
|
||||
cd $DAEMON_HOME
|
||||
nohup 2>/dev/null java -jar $DAEMON &>/dev/null &
|
||||
expr $! - 1 > $PIDFILE
|
||||
echo "Service started [`cat $PIDFILE`]\n"
|
||||
fi
|
||||
}
|
||||
|
||||
stop()
|
||||
{
|
||||
# If PIDFILE doesn't exists or PID isn't in running processes
|
||||
if [ ! -f "$PIDFILE" ] || ! kill -0 `cat "$PIDFILE"`
|
||||
then
|
||||
echo 'Service not running\n'
|
||||
else
|
||||
echo 'Stopping service...'
|
||||
# Send signal to end to the process
|
||||
kill -15 `cat "$PIDFILE"` && rm -f "$PIDFILE"
|
||||
echo 'Service stopped\n'
|
||||
fi
|
||||
}
|
||||
|
||||
status()
|
||||
{
|
||||
if [ -f $PIDFILE ] && kill -0 `cat $PIDFILE` 2>/dev/null
|
||||
then
|
||||
echo "Service is running (${GREEN}● active${NC})\n"
|
||||
else
|
||||
echo "Service not running (${RED}● inactive${NC})\n"
|
||||
fi
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
'start')
|
||||
start
|
||||
;;
|
||||
'stop')
|
||||
stop
|
||||
;;
|
||||
'status')
|
||||
status
|
||||
;;
|
||||
'restart')
|
||||
stop
|
||||
sleep 5
|
||||
start
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $SCRIPTNAME {start|stop|status|restart}\n" >&2
|
||||
exit 3
|
||||
;;
|
||||
esac
|
||||
exit 0
|
||||
28
src/main/bash/minecraft-server/minecraft-server-service
Normal file
28
src/main/bash/minecraft-server/minecraft-server-service
Normal file
@@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
|
||||
USER=takiguchi
|
||||
MINECRAFT_SERVER_PATH=/home/minecraft/server
|
||||
MINECRAFT_SHELL=minecraft-server.sh
|
||||
|
||||
case "$1" in
|
||||
'start')
|
||||
sudo -H -u $USER bash -c "$MINECRAFT_SERVER_PATH/$MINECRAFT_SHELL start"
|
||||
;;
|
||||
'stop')
|
||||
sudo -H -u $USER bash -c "$MINECRAFT_SERVER_PATH/$MINECRAFT_SHELL stop"
|
||||
;;
|
||||
'status')
|
||||
sudo -H -u $USER bash -c "$MINECRAFT_SERVER_PATH/$MINECRAFT_SHELL status"
|
||||
;;
|
||||
'restart')
|
||||
sudo -H -u $USER bash -c "$MINECRAFT_SERVER_PATH/$MINECRAFT_SHELL restart"
|
||||
;;
|
||||
*)
|
||||
# If no argument, we launch the app in case of server startup
|
||||
sudo -H -u $USER bash -c "$MINECRAFT_SERVER_PATH/$MINECRAFT_SHELL start &>/dev/null"
|
||||
# And show the script usage
|
||||
echo "Usage: /etc/init.d/minecraft {start|stop|status|restart}\n" >&2
|
||||
exit 3
|
||||
;;
|
||||
esac
|
||||
exit 0
|
||||
158
src/main/bash/minecraft-server/minecraft-server.sh
Normal file
158
src/main/bash/minecraft-server/minecraft-server.sh
Normal file
@@ -0,0 +1,158 @@
|
||||
#!/bin/sh
|
||||
# kFreeBSD do not accept scripts as interpreters, using #!/bin/sh and sourcing.
|
||||
if [ true != "$INIT_D_SCRIPT_SOURCED" ] ; then
|
||||
set "$0" "$@"; INIT_D_SCRIPT_SOURCED=true . /lib/init/init-d-script
|
||||
fi
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
NC='\033[0m'
|
||||
|
||||
DESC="The Minecraft server"
|
||||
NAME='minecraft-server'
|
||||
MINECRAFT_HOME='/home/minecraft'
|
||||
DAEMON_HOME="$MINECRAFT_HOME/server"
|
||||
DAEMON="$DAEMON_HOME/$NAME.jar"
|
||||
PIDFILE="$DAEMON_HOME/$NAME.pid"
|
||||
SCRIPTNAME="$DAEMON_HOME/$0"
|
||||
|
||||
# ***********************************************
|
||||
# Normal functions for bash commands.
|
||||
# ***********************************************
|
||||
start()
|
||||
{
|
||||
# If PIDFILE exists and PID is in running processes
|
||||
if [ -f $PIDFILE ] && kill -0 `cat $PIDFILE` 2>/dev/null
|
||||
then
|
||||
echo 'Service already running\n'
|
||||
else
|
||||
echo "Starting service $NAME"
|
||||
cd $DAEMON_HOME
|
||||
nohup 2>/dev/null java -jar $DAEMON &>/dev/null &
|
||||
expr $! - 1 > $PIDFILE
|
||||
echo "Service started [`cat $PIDFILE`]\n"
|
||||
fi
|
||||
}
|
||||
|
||||
stop()
|
||||
{
|
||||
# If PIDFILE doesn't exists or PID isn't in running processes
|
||||
if [ ! -f "$PIDFILE" ] || ! kill -0 `cat "$PIDFILE"`
|
||||
then
|
||||
echo 'Service not running\n'
|
||||
else
|
||||
echo 'Stopping service...'
|
||||
# Send signal to end to the process
|
||||
kill -15 `cat "$PIDFILE"` && rm -f "$PIDFILE"
|
||||
echo 'Service stopped\n'
|
||||
fi
|
||||
}
|
||||
|
||||
status()
|
||||
{
|
||||
if [ -f $PIDFILE ] && kill -0 `cat $PIDFILE` 2>/dev/null
|
||||
then
|
||||
echo "Service is running (${GREEN}● active${NC})\n"
|
||||
else
|
||||
echo "Service not running (${RED}● inactive${NC})\n"
|
||||
fi
|
||||
}
|
||||
|
||||
# ***********************************************
|
||||
# Commands used by Minager to drive the server.
|
||||
# ***********************************************
|
||||
api_check_error()
|
||||
{
|
||||
if [ $1 != 0 ]
|
||||
then
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
api_status()
|
||||
{
|
||||
if [ -f $PIDFILE ] && kill -0 `cat $PIDFILE` 2>/dev/null
|
||||
then
|
||||
echo 1 # Running
|
||||
else
|
||||
echo 0 # Stopped
|
||||
fi
|
||||
}
|
||||
|
||||
api_start() {
|
||||
# If PIDFILE exists and PID is in running processes
|
||||
if [ -f $PIDFILE ] && kill -0 `cat $PIDFILE` 2>/dev/null
|
||||
then
|
||||
exit 2 # STATE_UNCHANGED
|
||||
else
|
||||
cd $DAEMON_HOME
|
||||
api_check_error $?
|
||||
|
||||
nohup 2>/dev/null java -jar $DAEMON &>/dev/null &
|
||||
api_check_error $?
|
||||
|
||||
expr $! - 1 > $PIDFILE
|
||||
api_check_error $?
|
||||
fi
|
||||
}
|
||||
|
||||
api_stop()
|
||||
{
|
||||
# If PIDFILE doesn't exists or PID isn't in running processes
|
||||
if [ ! -f "$PIDFILE" ] || ! kill -0 `cat "$PIDFILE"`
|
||||
then
|
||||
exit 2 # STATE_UNCHANGED
|
||||
else
|
||||
# Send signal to end to the process
|
||||
kill -15 `cat "$PIDFILE"` && rm -f "$PIDFILE"
|
||||
api_check_error $?
|
||||
fi
|
||||
}
|
||||
|
||||
api_restart()
|
||||
{
|
||||
# Stop if running
|
||||
if [ $(api_status) = 1 ]
|
||||
then
|
||||
kill -15 `cat "$PIDFILE"` && rm -f "$PIDFILE"
|
||||
api_check_error $?
|
||||
fi
|
||||
|
||||
sleep 5
|
||||
api_start
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
'start')
|
||||
start
|
||||
;;
|
||||
'stop')
|
||||
stop
|
||||
;;
|
||||
'status')
|
||||
status
|
||||
;;
|
||||
'restart')
|
||||
stop
|
||||
sleep 5
|
||||
start
|
||||
;;
|
||||
'api_status')
|
||||
api_status
|
||||
;;
|
||||
'api_start')
|
||||
api_start
|
||||
;;
|
||||
'api_stop')
|
||||
api_stop
|
||||
;;
|
||||
'api_restart')
|
||||
api_restart
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $SCRIPTNAME {start|stop|status|restart}\n" >&2
|
||||
exit 3
|
||||
;;
|
||||
esac
|
||||
exit 0
|
||||
14
src/main/java/org/minager/MinagerApplication.java
Normal file
14
src/main/java/org/minager/MinagerApplication.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package org.minager;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableAutoConfiguration
|
||||
public class MinagerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(MinagerApplication.class, args);
|
||||
}
|
||||
}
|
||||
96
src/main/java/org/minager/account/AccountController.java
Normal file
96
src/main/java/org/minager/account/AccountController.java
Normal file
@@ -0,0 +1,96 @@
|
||||
package org.minager.account;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.minager.core.entities.dto.PasswordWrapperDTO;
|
||||
import org.minager.core.entities.dto.UserDTO;
|
||||
import org.minager.core.entities.persistence.User;
|
||||
import org.minager.core.security.TokenService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/account")
|
||||
public class AccountController {
|
||||
|
||||
private static final String HEADER_TOKEN = "token";
|
||||
|
||||
@Autowired
|
||||
private AccountService accountService;
|
||||
|
||||
@Autowired
|
||||
private TokenService tokenService;
|
||||
|
||||
/**
|
||||
* Log in the user in request body.
|
||||
*
|
||||
* @param pUser
|
||||
* The user to connect.
|
||||
* @param response
|
||||
* The reponse injected by Spring.
|
||||
* @return The connected user object.
|
||||
* @throws IOException
|
||||
* If credentials are bad.
|
||||
*/
|
||||
@PostMapping("/login")
|
||||
public UserDTO login(@RequestBody UserDTO pUser, HttpServletResponse response) throws IOException {
|
||||
return accountService.checkCredentials(response, pUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log out the user.
|
||||
*
|
||||
* @param pRequest
|
||||
* The request injected by Spring.
|
||||
*/
|
||||
@GetMapping("/logout")
|
||||
public void logout(HttpServletRequest pRequest) {
|
||||
tokenService.removeUser(pRequest.getHeader(HEADER_TOKEN));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the user password.
|
||||
*
|
||||
* @param pPasswordWrapper
|
||||
* The object which contains the old password for verification and
|
||||
* the new password to set to the user.
|
||||
* @param pRequest
|
||||
* The request injected by Spring.
|
||||
* @param pResponse
|
||||
* The reponse injected by Spring.
|
||||
* @throws IOException
|
||||
* If the old password doesn't match to the user password in
|
||||
* database.
|
||||
*/
|
||||
@PutMapping("/changePassword")
|
||||
public void changePassword(@RequestBody final PasswordWrapperDTO pPasswordWrapper,
|
||||
final HttpServletRequest pRequest,
|
||||
final HttpServletResponse pResponse) throws IOException {
|
||||
final Optional<User> connectedUser = tokenService.getAuthenticatedUserByToken(pRequest);
|
||||
if(connectedUser.isPresent()) {
|
||||
accountService.changePassword(connectedUser.get(), pPasswordWrapper, pResponse);
|
||||
} else {
|
||||
pResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/signin")
|
||||
public UserDTO signin(@RequestBody final UserDTO pUser, final HttpServletResponse pResponse) throws IOException {
|
||||
return accountService.signin(pUser, pResponse);
|
||||
}
|
||||
|
||||
@PutMapping("/")
|
||||
public void update(@RequestBody final UserDTO pUser, final HttpServletRequest pRequest,
|
||||
final HttpServletResponse pResponse) throws IOException {
|
||||
accountService.updateUser(pUser, pRequest, pResponse);
|
||||
}
|
||||
}
|
||||
124
src/main/java/org/minager/account/AccountService.java
Normal file
124
src/main/java/org/minager/account/AccountService.java
Normal file
@@ -0,0 +1,124 @@
|
||||
package org.minager.account;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.naming.AuthenticationException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.minager.core.entities.dto.PasswordWrapperDTO;
|
||||
import org.minager.core.entities.dto.UserDTO;
|
||||
import org.minager.core.entities.persistence.User;
|
||||
import org.minager.core.repositories.UserRepository;
|
||||
import org.minager.core.security.TokenService;
|
||||
import org.minager.core.utils.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@Service
|
||||
public class AccountService {
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Autowired
|
||||
private TokenService tokenService;
|
||||
|
||||
/**
|
||||
* Check the user credentials and generate him a token if they are correct.
|
||||
*
|
||||
* @param pUser
|
||||
* The user sent from client.
|
||||
* @return The user populated with the generated token.
|
||||
* @throws IOException
|
||||
* If the credentials are bad.
|
||||
* @throws AuthenticationException
|
||||
* If the credentials are wrong.
|
||||
*/
|
||||
public UserDTO checkCredentials(HttpServletResponse pResponse, UserDTO pUser) throws IOException {
|
||||
UserDTO result = null;
|
||||
|
||||
Optional<User> user = userRepository.findByEmail(pUser.getEmail());
|
||||
|
||||
if(user.isPresent() && StringUtils.compareHash(pUser.getPassword(), user.get().getPassword())) {
|
||||
tokenService.addUser(user.get());
|
||||
result = new UserDTO(user.get(), true);
|
||||
} else {
|
||||
pResponse.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void changePassword(final User pUser, final PasswordWrapperDTO pPasswordWrapper,
|
||||
final HttpServletResponse pResponse) throws IOException {
|
||||
if(pPasswordWrapper.getNewPassword().equals(pPasswordWrapper.getConfirmPassword())) {
|
||||
// We fetch the connected user from database to get his hashed password
|
||||
final Optional<User> userFromDb = userRepository.findById(pUser.getId());
|
||||
if(userFromDb.isPresent() && StringUtils.compareHash(pPasswordWrapper.getOldPassword(),
|
||||
userFromDb.get().getPassword())) {
|
||||
userFromDb.get().setPassword(StringUtils.hashPassword(pPasswordWrapper.getNewPassword()));
|
||||
userRepository.save(userFromDb.get());
|
||||
} else {
|
||||
pResponse.sendError(HttpServletResponse.SC_FORBIDDEN,
|
||||
"Le mot de passe saisi ne correspond pas au votre.");
|
||||
}
|
||||
} else {
|
||||
pResponse.sendError(HttpServletResponse.SC_BAD_REQUEST,
|
||||
"Le mot de passe saisi ne correspond pas au votre.");
|
||||
}
|
||||
}
|
||||
|
||||
public UserDTO signin(final UserDTO pUser, final HttpServletResponse pResponse) throws IOException {
|
||||
User user = new User();
|
||||
|
||||
if(pUser.getName() == null || pUser.getEmail() == null || pUser.getPassword() == null || "".equals(pUser.getPassword().trim())) {
|
||||
pResponse.sendError(HttpServletResponse.SC_BAD_REQUEST);
|
||||
} else if(userRepository.findByEmail(pUser.getEmail()).isPresent()) {
|
||||
pResponse.sendError(HttpServletResponse.SC_CONFLICT);
|
||||
} else {
|
||||
user.setName(pUser.getName());
|
||||
user.setEmail(pUser.getEmail());
|
||||
user.setPassword(StringUtils.hashPassword(pUser.getPassword()));
|
||||
user.setInscriptionDate(new Date());
|
||||
userRepository.save(user);
|
||||
}
|
||||
|
||||
return new UserDTO(user);
|
||||
}
|
||||
|
||||
public void updateUser(final UserDTO pUser, final HttpServletRequest pRequest,
|
||||
final HttpServletResponse pResponse) throws IOException {
|
||||
final Optional<User> connectedUserOpt = tokenService.getAuthenticatedUserByToken(pRequest);
|
||||
if(connectedUserOpt.isPresent()) {
|
||||
final User connectedUser = connectedUserOpt.get();
|
||||
|
||||
final Optional<User> userFromDb = userRepository.findByEmail(pUser.getEmail());
|
||||
|
||||
/*
|
||||
* If a user is returned by the repository, that's the email adress is used, but
|
||||
* if it is not the same as the connected user, that's the email adress
|
||||
* corresponds to another user. So a 409 error is sent. Otherwise, if no user is
|
||||
* returned by the repository, that's the email adress is free to be used. So,
|
||||
* user can change him email adress.
|
||||
*/
|
||||
if(userFromDb.isPresent() && !connectedUser.getEmail().equals(userFromDb.get().getEmail())) {
|
||||
pResponse.sendError(HttpServletResponse.SC_CONFLICT);
|
||||
} else {
|
||||
connectedUser.setName(pUser.getName());
|
||||
connectedUser.setEmail(pUser.getEmail());
|
||||
|
||||
userRepository.save(connectedUser);
|
||||
}
|
||||
} else {
|
||||
pResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
}
|
||||
129
src/main/java/org/minager/core/AbstractFilter.java
Normal file
129
src/main/java/org/minager/core/AbstractFilter.java
Normal file
@@ -0,0 +1,129 @@
|
||||
package org.minager.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.minager.core.security.Route;
|
||||
import org.minager.core.utils.StringUtils;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
/**
|
||||
* Base class for all filters of the application.<br/>
|
||||
* <br/>
|
||||
* The children classes have to implements the method
|
||||
* {@link AbstractFilter#getClass()} to set the URLs filtered (with all or some
|
||||
* http methods), and the method
|
||||
* {@link AbstractFilter#filter(HttpServletRequest, ServletResponse, FilterChain)}
|
||||
* to define the filter processing.
|
||||
*
|
||||
* @author Takiguchi
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractFilter implements Filter {
|
||||
|
||||
/** Regex url path prefix for method {@link this#isRequestFiltered(String)}. */
|
||||
private static final String PREFIX_URL_PATH = "https?:\\/\\/.*(:\\d{0,5})?";
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of routes which will be processed by the filter.
|
||||
*
|
||||
* @return The routes.
|
||||
*/
|
||||
protected abstract List<Route> getRoutes();
|
||||
|
||||
/**
|
||||
* Filter actions for its processing.
|
||||
*
|
||||
* @param request
|
||||
* The http request.
|
||||
* @param response
|
||||
* The response.
|
||||
* @param chain
|
||||
* The chain.
|
||||
*/
|
||||
protected abstract void filter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException;
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||
|
||||
if(isRequestFiltered(httpRequest.getRequestURL().toString(), httpRequest.getMethod())) {
|
||||
filter(httpRequest, (HttpServletResponse) response, chain);
|
||||
} else {
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the url is allowed with the given method in parameters.
|
||||
*
|
||||
* @param pRequestUrl
|
||||
* The url request.
|
||||
* @param pRequestMethod
|
||||
* The http method of the request.
|
||||
* @return {@code true} if the url is allowed with the method, {@code false}
|
||||
* otherwise.
|
||||
*/
|
||||
boolean isRequestFiltered(final String pRequestUrl, final String pRequestMethod) {
|
||||
boolean result = false;
|
||||
|
||||
for(final Route route : getRoutes()) {
|
||||
/*
|
||||
* Check urls matching, and if the method of the route isn't set, all methods
|
||||
* are allowed. Otherwise, we check the methods too.
|
||||
*/
|
||||
if(Pattern.matches(StringUtils.concat(PREFIX_URL_PATH, route.getUrl()), pRequestUrl)) {
|
||||
if(!route.getMethod().isPresent() || isMethodFiltered(route, pRequestMethod)) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the route do filter the method in parameters.
|
||||
*
|
||||
* @param pRoute
|
||||
* The registered route.
|
||||
* @param pRequestMethod
|
||||
* The http method to check with the registered route.
|
||||
*/
|
||||
boolean isMethodFiltered(final Route pRoute, final String pRequestMethod) {
|
||||
boolean result = false;
|
||||
|
||||
if(pRoute.getMethod().isPresent()) {
|
||||
for(final HttpMethod routeMethod : pRoute.getMethod().get()) {
|
||||
if(routeMethod.name().equals(pRequestMethod)) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
42
src/main/java/org/minager/core/config/JpaConfiguration.java
Normal file
42
src/main/java/org/minager/core/config/JpaConfiguration.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package org.minager.core.config;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||
import org.springframework.boot.jdbc.DataSourceBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
@Configuration
|
||||
@EntityScan("org.minager")
|
||||
@EnableTransactionManagement
|
||||
@EnableJpaRepositories("org.minager")
|
||||
@PropertySource("classpath:application.properties")
|
||||
public class JpaConfiguration {
|
||||
|
||||
@Value("${spring.datasource.driverClassName}")
|
||||
private String driverClassName;
|
||||
|
||||
@Value("${spring.datasource.url}")
|
||||
private String url;
|
||||
|
||||
@Value("${spring.datasource.username}")
|
||||
private String username;
|
||||
|
||||
@Value("${spring.datasource.password}")
|
||||
private String password;
|
||||
|
||||
@Bean(name="dataSource")
|
||||
public DataSource getDataSource() {
|
||||
return DataSourceBuilder.create()
|
||||
.username(username)
|
||||
.password(password)
|
||||
.url(url)
|
||||
.driverClassName(driverClassName)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
18
src/main/java/org/minager/core/constant/ResultCode.java
Normal file
18
src/main/java/org/minager/core/constant/ResultCode.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package org.minager.core.constant;
|
||||
|
||||
public enum ResultCode {
|
||||
SUCCESS(0),
|
||||
FAILED(1),
|
||||
STATE_UNCHANGED(2),
|
||||
ILLEGAL_ARGUMENT(3);
|
||||
|
||||
private int val;
|
||||
|
||||
private ResultCode(final int pVal) {
|
||||
val = pVal;
|
||||
}
|
||||
|
||||
public int val() {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package org.minager.core.entities.business;
|
||||
|
||||
/**
|
||||
* Object that contains the results of a system command.
|
||||
*
|
||||
* @author takiguchi
|
||||
*
|
||||
*/
|
||||
public class SystemResult {
|
||||
/** The result code of the command. */
|
||||
private int resultCode;
|
||||
/** The standard output of the command. */
|
||||
private StringBuilder stdOut;
|
||||
/** The standard errors output of the command. */
|
||||
private StringBuilder stdErr;
|
||||
|
||||
public SystemResult() {
|
||||
super();
|
||||
stdOut = new StringBuilder();
|
||||
stdErr = new StringBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the result code.
|
||||
*
|
||||
* @return the result code
|
||||
*/
|
||||
public int getResultCode() {
|
||||
return resultCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the result code.
|
||||
*
|
||||
* @param resultCode
|
||||
* the new result code
|
||||
*/
|
||||
public void setResultCode(int resultCode) {
|
||||
this.resultCode = resultCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the std out.
|
||||
*
|
||||
* @return the std out
|
||||
*/
|
||||
public String getStdOut() {
|
||||
return stdOut.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an output line to the {@code stdOut}.
|
||||
*
|
||||
* @param pLine
|
||||
* The output line to append.
|
||||
*/
|
||||
public void addOutputLine(final String pLine) {
|
||||
stdOut.append(pLine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the std out.
|
||||
*
|
||||
* @param stdOut
|
||||
* the new std out
|
||||
*/
|
||||
public void setStdOut(String stdOut) {
|
||||
this.stdOut = new StringBuilder(stdOut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the std err.
|
||||
*
|
||||
* @return the std err
|
||||
*/
|
||||
public String getStdErr() {
|
||||
return stdErr.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an error line to the {@code stdErr}.
|
||||
*
|
||||
* @param pLine
|
||||
* The error line to append.
|
||||
*/
|
||||
public void addErrorLine(final String pLine) {
|
||||
stdErr.append(pLine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the std err.
|
||||
*
|
||||
* @param stdErr
|
||||
* the new std err
|
||||
*/
|
||||
public void setStdErr(String stdErr) {
|
||||
this.stdErr = new StringBuilder(stdErr);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.minager.core.entities.dto;
|
||||
|
||||
public class PasswordWrapperDTO {
|
||||
|
||||
private String oldPassword;
|
||||
|
||||
private String newPassword;
|
||||
|
||||
private String confirmPassword;
|
||||
|
||||
public String getOldPassword() {
|
||||
return oldPassword;
|
||||
}
|
||||
|
||||
public void setOldPassword(String oldPassword) {
|
||||
this.oldPassword = oldPassword;
|
||||
}
|
||||
|
||||
public String getNewPassword() {
|
||||
return newPassword;
|
||||
}
|
||||
|
||||
public void setNewPassword(String newPassword) {
|
||||
this.newPassword = newPassword;
|
||||
}
|
||||
|
||||
public String getConfirmPassword() {
|
||||
return confirmPassword;
|
||||
}
|
||||
|
||||
public void setConfirmPassword(String confirmPassword) {
|
||||
this.confirmPassword = confirmPassword;
|
||||
}
|
||||
}
|
||||
97
src/main/java/org/minager/core/entities/dto/UserDTO.java
Normal file
97
src/main/java/org/minager/core/entities/dto/UserDTO.java
Normal file
@@ -0,0 +1,97 @@
|
||||
package org.minager.core.entities.dto;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.minager.core.entities.persistence.User;
|
||||
|
||||
public class UserDTO {
|
||||
|
||||
private String key;
|
||||
|
||||
private String name;
|
||||
|
||||
private String email;
|
||||
|
||||
private String password;
|
||||
|
||||
private String image;
|
||||
|
||||
private Date inscriptionDate;
|
||||
|
||||
private String token;
|
||||
|
||||
public UserDTO() {
|
||||
super();
|
||||
}
|
||||
|
||||
public UserDTO(final User pUser) {
|
||||
key = pUser.getKey();
|
||||
name = pUser.getName();
|
||||
email = pUser.getEmail();
|
||||
image = pUser.getImage();
|
||||
inscriptionDate = pUser.getInscriptionDate();
|
||||
}
|
||||
|
||||
public UserDTO(final User pUser, final boolean pWithToken) {
|
||||
this(pUser);
|
||||
if(pWithToken) {
|
||||
token = pUser.getToken().getValue();
|
||||
}
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getImage() {
|
||||
return image;
|
||||
}
|
||||
|
||||
public void setImage(String image) {
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
public Date getInscriptionDate() {
|
||||
return inscriptionDate;
|
||||
}
|
||||
|
||||
public void setInscriptionDate(Date inscriptionDate) {
|
||||
this.inscriptionDate = inscriptionDate;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
}
|
||||
126
src/main/java/org/minager/core/entities/persistence/User.java
Normal file
126
src/main/java/org/minager/core/entities/persistence/User.java
Normal file
@@ -0,0 +1,126 @@
|
||||
package org.minager.core.entities.persistence;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import org.hibernate.annotations.Generated;
|
||||
import org.hibernate.annotations.GenerationTime;
|
||||
import org.minager.core.entities.security.Token;
|
||||
|
||||
@Entity
|
||||
@Table(name="`user`")
|
||||
public class User implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/* ******************* */
|
||||
/* Attributes */
|
||||
/* ******************* */
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator="user_id_seq")
|
||||
@SequenceGenerator(name="user_id_seq", sequenceName="user_id_seq", allocationSize=1)
|
||||
private Long id;
|
||||
|
||||
// This annotation serves to fetch the attribute after an insert into db
|
||||
@Generated(GenerationTime.ALWAYS)
|
||||
private String key;
|
||||
|
||||
private String name;
|
||||
|
||||
private String email;
|
||||
|
||||
private String password;
|
||||
|
||||
private String image;
|
||||
|
||||
@Column(name = "inscription_date")
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date inscriptionDate;
|
||||
|
||||
/** Authentication token. */
|
||||
private transient Token token;
|
||||
|
||||
/* ******************* */
|
||||
/* Constructors */
|
||||
/* ******************* */
|
||||
public User() {
|
||||
super();
|
||||
token = new Token();
|
||||
}
|
||||
|
||||
/* ******************* */
|
||||
/* Getters & Setters */
|
||||
/* ******************* */
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
if(this.id != null) {
|
||||
throw new IllegalAccessError("It's not allowed to rewrite the id entity.");
|
||||
}
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getImage() {
|
||||
return image;
|
||||
}
|
||||
|
||||
public void setImage(String image) {
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
public Date getInscriptionDate() {
|
||||
return inscriptionDate;
|
||||
}
|
||||
|
||||
public void setInscriptionDate(Date inscriptionDate) {
|
||||
this.inscriptionDate = inscriptionDate;
|
||||
}
|
||||
|
||||
public Token getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
}
|
||||
82
src/main/java/org/minager/core/entities/security/Token.java
Normal file
82
src/main/java/org/minager/core/entities/security/Token.java
Normal file
@@ -0,0 +1,82 @@
|
||||
package org.minager.core.entities.security;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Calendar;
|
||||
|
||||
public class Token {
|
||||
/** The metric in which the validation delay is defined. */
|
||||
private static final int METRIC = Calendar.MINUTE;
|
||||
/** Number of {@link METRIC} after that the token become invalid. */
|
||||
private static final int DELAY = 30;
|
||||
|
||||
/** The Constant BITS_NUMBER. */
|
||||
private static final int BITS_NUMBER = 1000;
|
||||
/** The Constant RADIX. */
|
||||
private static final int RADIX = 32;
|
||||
|
||||
/** The value. */
|
||||
private String value;
|
||||
|
||||
/**
|
||||
* Last access date. For each request to the server, this date is consulted
|
||||
* and if the valid delay is ok, this date must be updated.
|
||||
*/
|
||||
private Calendar lastAccess;
|
||||
|
||||
/**
|
||||
* Instantiates a new token.
|
||||
*/
|
||||
public Token() {
|
||||
super();
|
||||
value = new BigInteger(BITS_NUMBER, new SecureRandom()).toString(RADIX);
|
||||
lastAccess = Calendar.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value.
|
||||
*
|
||||
* @return the value
|
||||
*/
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the last access date.
|
||||
*
|
||||
* @return the last access date
|
||||
*/
|
||||
public Calendar getLastAccess() {
|
||||
return lastAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the last access date.
|
||||
*/
|
||||
public void setLastAccess() {
|
||||
lastAccess = Calendar.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate if the token is still valid.<br/>
|
||||
* A token is valid is its {@link Token#lastAccess} is after the current
|
||||
* date minus the {@link Token#DELAY} {@link Token#METRIC}.<br/>
|
||||
* <br/>
|
||||
* Example:<br/>
|
||||
* {@link Token#DELAY} = 30 and {@link Token#METRIC} =
|
||||
* {@link Calendar#MINUTE}.<br/>
|
||||
* A token is valid only on the 30 minutes after its
|
||||
* {@link Token#lastAccess}.<br/>
|
||||
* If the current date-time minus the 30 minutes is before the
|
||||
* {@link Token#lastAccess}, the token is still valid.
|
||||
*
|
||||
* @return {@code true} if the token is still valid, {@code false}
|
||||
* otherwise.
|
||||
*/
|
||||
public boolean isValid() {
|
||||
final Calendar lastTimeValidation = Calendar.getInstance();
|
||||
lastTimeValidation.add(METRIC, -DELAY);
|
||||
return lastAccess.getTime().after(lastTimeValidation.getTime());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.minager.core.repositories;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.transaction.Transactional;
|
||||
|
||||
import org.minager.core.entities.persistence.User;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface UserRepository extends CrudRepository<User, Long> {
|
||||
|
||||
Optional<User> findByEmail(@Param("email") final String pEmail);
|
||||
|
||||
/**
|
||||
* Checks if the password in parameters is the passwords of the user in
|
||||
* database.
|
||||
*
|
||||
* @param pId
|
||||
* The user id.
|
||||
* @param pPassword
|
||||
* The password to check.
|
||||
* @return {@code true} if the password is the user password in database,
|
||||
* {@code false} otherwise.
|
||||
*/
|
||||
@Query(value = "SELECT CASE WHEN EXISTS(" +
|
||||
" SELECT id FROM \"user\" WHERE id = :id AND password = :password" +
|
||||
") THEN TRUE ELSE FALSE END", nativeQuery = true)
|
||||
boolean checkPassword(@Param("id") final Long pId, @Param("password") final String pPassword);
|
||||
|
||||
@Query(value = "UPDATE \"user\" SET password = :password WHERE id = :id", nativeQuery = true)
|
||||
@Transactional
|
||||
@Modifying
|
||||
void updatePassword(@Param("id") final Long pId, @Param("password") final String pPassword);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.minager.core.security;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.minager.core.AbstractFilter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public class AuthenticationFilter extends AbstractFilter {
|
||||
|
||||
private static final String HTTP_OPTIONS = "OPTIONS";
|
||||
|
||||
private static final String HEADER_TOKEN = "token";
|
||||
|
||||
@Autowired
|
||||
private TokenService tokenService;
|
||||
|
||||
@Override
|
||||
protected List<Route> getRoutes() {
|
||||
return Arrays.asList(
|
||||
new Route("\\/api\\/server\\/start"),
|
||||
new Route("\\/api\\/server\\/stop"),
|
||||
new Route("\\/api\\/server\\/restart")
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void filter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
if(HTTP_OPTIONS.equals(request.getMethod()) || tokenService.isUserConnected(request.getHeader(HEADER_TOKEN))) {
|
||||
chain.doFilter(request, response);
|
||||
} else {
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
44
src/main/java/org/minager/core/security/CorsFilter.java
Normal file
44
src/main/java/org/minager/core/security/CorsFilter.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package org.minager.core.security;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public class CorsFilter implements Filter {
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||
httpResponse.addHeader("Access-Control-Allow-Origin", "*");
|
||||
httpResponse.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, PATCH, HEAD");
|
||||
httpResponse.addHeader("Access-Control-Allow-Headers", "Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");
|
||||
httpResponse.addHeader("Access-Control-Expose-Headers", "Access-Control-Allow-Origin, Access-Control-Allow-Credentials");
|
||||
httpResponse.addHeader("Access-Control-Allow-Credentials", "true");
|
||||
httpResponse.addIntHeader("Access-Control-Max-Age", 10);
|
||||
chain.doFilter(request, httpResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
}
|
||||
71
src/main/java/org/minager/core/security/Route.java
Normal file
71
src/main/java/org/minager/core/security/Route.java
Normal file
@@ -0,0 +1,71 @@
|
||||
package org.minager.core.security;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
/**
|
||||
* Route for filter matching.
|
||||
*
|
||||
* @author Takiguchi
|
||||
*
|
||||
*/
|
||||
public class Route {
|
||||
/** The regex to match urls. */
|
||||
private String url;
|
||||
/** The http method to match. Use a {@link Optional#empty()} to match all methods. */
|
||||
private Optional<List<HttpMethod>> method;
|
||||
|
||||
/**
|
||||
* Instanciate a vierge route.
|
||||
*/
|
||||
public Route() {
|
||||
super();
|
||||
url = "";
|
||||
method = Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instanciate a route for all http methods.
|
||||
*
|
||||
* @param pUrl
|
||||
* The regex to match urls.
|
||||
*/
|
||||
public Route(final String pUrl) {
|
||||
this();
|
||||
this.url = pUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instanciate a route for methods given in parameters
|
||||
*
|
||||
* @param pUrl
|
||||
* The regex to match urls.
|
||||
* @param pMethod
|
||||
* The http method to match. Use a {@link Optional#empty()} to match
|
||||
* all methods.
|
||||
*/
|
||||
public Route(final String pUrl, final HttpMethod... pMethods) {
|
||||
this(pUrl);
|
||||
this.method = Optional.of(Arrays.asList(pMethods));
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public Optional<List<HttpMethod>> getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public void setMethod(HttpMethod pMethods) {
|
||||
this.method = Optional.of(Arrays.asList(pMethods));
|
||||
}
|
||||
|
||||
}
|
||||
132
src/main/java/org/minager/core/security/TokenService.java
Normal file
132
src/main/java/org/minager/core/security/TokenService.java
Normal file
@@ -0,0 +1,132 @@
|
||||
package org.minager.core.security;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.minager.core.entities.persistence.User;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class TokenService {
|
||||
/** Map of connected users. */
|
||||
private static final Map<String, User> connectedUsers;
|
||||
|
||||
private static final String HEADER_TOKEN = "token";
|
||||
|
||||
private static final long INTERVAL_USER_CLEANING = 5;
|
||||
|
||||
private static final long INTERVAL_USER_CLEANING_VAL = INTERVAL_USER_CLEANING * 60 * 1000;
|
||||
|
||||
private Date lastUsersCleaning;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
static {
|
||||
connectedUsers = new TreeMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the token matches to a user session, and if it is still valid.
|
||||
*
|
||||
* @param pToken
|
||||
* The token to check.
|
||||
* @return {@code true} if the token is still valid, {@code false}
|
||||
* otherwise.
|
||||
*/
|
||||
public boolean isUserConnected(final String pToken) {
|
||||
boolean result = false;
|
||||
|
||||
if (pToken != null && connectedUsers.containsKey(pToken)) {
|
||||
if (connectedUsers.get(pToken).getToken().isValid()) {
|
||||
result = true;
|
||||
} else {
|
||||
connectedUsers.remove(pToken);
|
||||
}
|
||||
}
|
||||
|
||||
// clear all the expired sessions
|
||||
final Date now = new Date();
|
||||
if(lastUsersCleaning == null || now.getTime() - lastUsersCleaning.getTime() >= INTERVAL_USER_CLEANING_VAL) {
|
||||
new Thread(this::clearExpiredUsers).start();
|
||||
lastUsersCleaning = now;
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove from the connected users map all the elements which their token is
|
||||
* expired.
|
||||
*/
|
||||
private void clearExpiredUsers() {
|
||||
synchronized (this) {
|
||||
List<User> usersToRemove = new LinkedList<>();
|
||||
connectedUsers.entrySet().stream().forEach(user -> {
|
||||
if(!user.getValue().getToken().isValid()) {
|
||||
usersToRemove.add(user.getValue());
|
||||
}
|
||||
});
|
||||
usersToRemove.stream().map(User::getKey).forEach(connectedUsers::remove);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the user to the connected users map.
|
||||
*
|
||||
* @param pUser
|
||||
* The user to add.
|
||||
*/
|
||||
public void addUser(final User pUser) {
|
||||
if(connectedUsers.get(pUser.getToken().getValue()) == null) {
|
||||
connectedUsers.put(pUser.getToken().getValue(), pUser);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the user token last access date in the token service.
|
||||
*
|
||||
* @param pToken
|
||||
* The user token.
|
||||
*/
|
||||
public void refreshUserToken(final String pToken) {
|
||||
final User user = connectedUsers.get(pToken);
|
||||
if(user != null) {
|
||||
user.getToken().setLastAccess();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the user to the connected users map.
|
||||
*
|
||||
* @param pUser
|
||||
* The user to remove.
|
||||
*/
|
||||
public void removeUser(final User pUser) {
|
||||
removeUser(pUser.getToken().getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the user associated to the token given in parameters, from the
|
||||
* connected users map.
|
||||
*
|
||||
* @param pToken
|
||||
* The user to delete token.
|
||||
*/
|
||||
public void removeUser(final String pToken) {
|
||||
if(pToken != null && connectedUsers.containsKey(pToken)) {
|
||||
connectedUsers.remove(pToken);
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<User> getAuthenticatedUserByToken(final HttpServletRequest pRequest) {
|
||||
return Optional.ofNullable(connectedUsers.get(pRequest.getHeader(HEADER_TOKEN)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package org.minager.core.services.business;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import org.minager.core.entities.business.SystemResult;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class SystemService {
|
||||
|
||||
public SystemResult executeCommand(final String pCommand, final String... pArgs) {
|
||||
final String command = buildCommand(pCommand, pArgs);
|
||||
|
||||
final SystemResult commandResults = new SystemResult();
|
||||
|
||||
try {
|
||||
// Process creation and execution of the command.
|
||||
final Process process = Runtime.getRuntime().exec(command);
|
||||
|
||||
// Waiting the end of the command execution.
|
||||
process.waitFor();
|
||||
|
||||
// Getting of the stantard command output, and the standard error output.
|
||||
BufferedReader outputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
|
||||
|
||||
String tempLine = null;
|
||||
while((tempLine = outputReader.readLine()) != null) {
|
||||
commandResults.addOutputLine(tempLine);
|
||||
}
|
||||
|
||||
while((tempLine = errorReader.readLine()) != null) {
|
||||
commandResults.addErrorLine(tempLine);
|
||||
}
|
||||
|
||||
commandResults.setResultCode(process.exitValue());
|
||||
} catch(IOException | InterruptedException ex) {
|
||||
// LOGGER.error(StringUtils.concat("Une erreur est survenue lors de l'exécution de la commande \"", command,
|
||||
// "\"."), ex);
|
||||
System.err.println(ex.getMessage());
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
return commandResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the command in form of one string from the parameters.
|
||||
*
|
||||
* @param pCommand
|
||||
* The command to execute.
|
||||
* @param pArgs
|
||||
* The command arguments, could be {@code null}.
|
||||
* @return The command built.
|
||||
*/
|
||||
private String buildCommand(final String pCommand, final Object... pArgs) {
|
||||
final StringBuilder command = new StringBuilder(pCommand);
|
||||
|
||||
if (pArgs != null) {
|
||||
for (final Object arg : pArgs) {
|
||||
command.append(" ").append(arg);
|
||||
}
|
||||
}
|
||||
|
||||
return command.toString();
|
||||
}
|
||||
}
|
||||
82
src/main/java/org/minager/core/utils/RegexUtils.java
Normal file
82
src/main/java/org/minager/core/utils/RegexUtils.java
Normal file
@@ -0,0 +1,82 @@
|
||||
package org.minager.core.utils;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public final class RegexUtils {
|
||||
|
||||
private static final String EMAIL_REGEX = "^.*@.*\\..{2,}$";
|
||||
private static final String LOWER_LETTERS_REGEX = ".*[a-z].*";
|
||||
private static final String UPPER_LETTERS_REGEX = ".*[A-Z].*";
|
||||
private static final String NUMBER_REGEX = ".*[0-9].*";
|
||||
private static final String SPECIAL_CHAR_REGEX = ".*\\W.*";
|
||||
private static final String NUMBER_ONLY_REGEX = "^[0-9]+$";
|
||||
|
||||
// La portée "package" permet à la classe StringUtils d'utiliser les patterns
|
||||
// suivants :
|
||||
static final Pattern EMAIL_PATTERN;
|
||||
static final Pattern LOWER_LETTERS_PATTERN;
|
||||
static final Pattern UPPER_LETTERS_PATTERN;
|
||||
static final Pattern NUMBER_PATTERN;
|
||||
static final Pattern SPECIAL_CHAR_PATTERN;
|
||||
static final Pattern NUMBER_ONLY_PATTERN;
|
||||
|
||||
static {
|
||||
EMAIL_PATTERN = Pattern.compile(EMAIL_REGEX);
|
||||
LOWER_LETTERS_PATTERN = Pattern.compile(LOWER_LETTERS_REGEX);
|
||||
UPPER_LETTERS_PATTERN = Pattern.compile(UPPER_LETTERS_REGEX);
|
||||
NUMBER_PATTERN = Pattern.compile(NUMBER_REGEX);
|
||||
SPECIAL_CHAR_PATTERN = Pattern.compile(SPECIAL_CHAR_REGEX);
|
||||
NUMBER_ONLY_PATTERN = Pattern.compile(NUMBER_ONLY_REGEX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Chekcs if {@code pString} corresponds to an email address.
|
||||
*
|
||||
* @param pString
|
||||
* The string which should be an email address.
|
||||
* @return {@code true} if {@link pString} corresponds to an email address,
|
||||
* {@code false} otherwise.
|
||||
*/
|
||||
public static boolean isEmail(final String pString) {
|
||||
return EMAIL_PATTERN.matcher(pString).find();
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the sequences of {@code pString} matched by the {@code pRegex}
|
||||
* with the {@code pReplacingString}.
|
||||
*
|
||||
* @param pString
|
||||
* The string to update.
|
||||
* @param pRegex
|
||||
* The regex to match the sentences to replace.
|
||||
* @param pReplacingString
|
||||
* The string to replace the sentences which match with the
|
||||
* regex.
|
||||
* @return The new string.
|
||||
*/
|
||||
public static String replaceSequence(final String pString,
|
||||
final String pRegex, final String pReplacingString) {
|
||||
return Pattern.compile(pRegex).matcher(pString)
|
||||
.replaceAll(pReplacingString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if {@code pString} corresponds to a number.
|
||||
*
|
||||
* @param pString
|
||||
* The string which should be a number.
|
||||
* @return {@code true} if {@code pString} corresponds to a number,
|
||||
* {@code false} otherwise.
|
||||
*/
|
||||
public static boolean isNumber(final String pString) {
|
||||
return NUMBER_ONLY_PATTERN.matcher(pString).find();
|
||||
}
|
||||
|
||||
public static String getGroup(final String regex, final int numeroGroupe, final String chaine) {
|
||||
final Pattern pattern = Pattern.compile(regex);
|
||||
final Matcher matcher = pattern.matcher(chaine);
|
||||
matcher.find();
|
||||
return matcher.group(numeroGroupe);
|
||||
}
|
||||
}
|
||||
93
src/main/java/org/minager/core/utils/StringUtils.java
Normal file
93
src/main/java/org/minager/core/utils/StringUtils.java
Normal file
@@ -0,0 +1,93 @@
|
||||
package org.minager.core.utils;
|
||||
|
||||
import org.mindrot.jbcrypt.BCrypt;
|
||||
|
||||
/**
|
||||
* Generic methods about {@link String} class.
|
||||
*
|
||||
* @author takiguchi
|
||||
*
|
||||
*/
|
||||
public final class StringUtils {
|
||||
|
||||
/**
|
||||
* Indicate if {@code pString} is null or just composed of spaces.
|
||||
*
|
||||
* @param pString
|
||||
* The string to test.
|
||||
* @return {@code true} if {@code pString} is null or just composed of
|
||||
* spaces, {@code false} otherwise.
|
||||
*/
|
||||
public static boolean isNull(final String chaine) {
|
||||
return chaine == null || chaine.trim().length() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash the password given into parameters.
|
||||
*
|
||||
* @param pPassword The password to hash.
|
||||
* @return The password hashed.
|
||||
*/
|
||||
public static String hashPassword(final String pPassword) {
|
||||
return hashString(pPassword, 10);
|
||||
}
|
||||
|
||||
public static String hashString(final String pString, final int pSalt) {
|
||||
return BCrypt.hashpw(pString, BCrypt.gensalt(pSalt));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the password and the hashed string given into parameters.
|
||||
*
|
||||
* @param pPassword
|
||||
* The password to compare to the hashed string.
|
||||
* @param pHashToCompare
|
||||
* The hashed string to compare to the password.
|
||||
* @return {@code true} if the password matches to the hashed string.
|
||||
*/
|
||||
public static boolean compareHash(final String pPassword, final String pHashToCompare) {
|
||||
return BCrypt.checkpw(pPassword, pHashToCompare);
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate the parameters to form just one single string.
|
||||
*
|
||||
* @param pArgs
|
||||
* The strings to concatenate.
|
||||
* @return The parameters concatenated.
|
||||
*/
|
||||
public static String concat(final Object... pArgs) {
|
||||
final StringBuilder result = new StringBuilder();
|
||||
for (final Object arg : pArgs) {
|
||||
result.append(arg);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public static String printStrings(final String... pStrings) {
|
||||
final StringBuilder result = new StringBuilder();
|
||||
for (int i = 0 ; i < pStrings.length ; i++) {
|
||||
result.append(pStrings[i]);
|
||||
if(i < pStrings.length - 1) {
|
||||
result.append(",");
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public static boolean containLowercase(final String pString) {
|
||||
return RegexUtils.LOWER_LETTERS_PATTERN.matcher(pString).find();
|
||||
}
|
||||
|
||||
public static boolean containUppercase(final String pString) {
|
||||
return RegexUtils.UPPER_LETTERS_PATTERN.matcher(pString).find();
|
||||
}
|
||||
|
||||
public static boolean containNumber(final String pString) {
|
||||
return RegexUtils.NUMBER_PATTERN.matcher(pString).find();
|
||||
}
|
||||
|
||||
public static boolean containSpecialChar(final String pString) {
|
||||
return RegexUtils.SPECIAL_CHAR_PATTERN.matcher(pString).find();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package org.minager.serverhandling;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/server")
|
||||
public class ServerHandlingController {
|
||||
@Autowired
|
||||
private ServerHandlingService serverHandlingService;
|
||||
|
||||
@GetMapping("/status")
|
||||
public int getStatus() {
|
||||
return serverHandlingService.getStatus();
|
||||
}
|
||||
|
||||
@PostMapping("/start")
|
||||
public void startServer(final HttpServletResponse pResponse) throws IOException {
|
||||
serverHandlingService.startServer(pResponse);
|
||||
}
|
||||
|
||||
@PostMapping("/stop")
|
||||
public void stopServer(final HttpServletResponse pResponse) throws IOException {
|
||||
serverHandlingService.stopServer(pResponse);
|
||||
}
|
||||
|
||||
@PostMapping("/restart")
|
||||
public void restartServer(final HttpServletResponse pResponse) throws IOException {
|
||||
serverHandlingService.restartServer(pResponse);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package org.minager.serverhandling;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.minager.core.constant.ResultCode;
|
||||
import org.minager.core.entities.business.SystemResult;
|
||||
import org.minager.core.services.business.SystemService;
|
||||
import org.minager.core.utils.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class ServerHandlingService {
|
||||
|
||||
@Autowired
|
||||
private SystemService systemService;
|
||||
|
||||
@Value("${minecraft.server.path}")
|
||||
private String minecraftServerPath;
|
||||
|
||||
@Value("${minecraft.server.shell.name}")
|
||||
private String minecraftServerShellName;
|
||||
|
||||
private String buildMinecraftServerShellPath() {
|
||||
return StringUtils.concat(minecraftServerPath,
|
||||
minecraftServerPath.charAt(minecraftServerPath.length() - 1) == '/' ? "" : '/',
|
||||
minecraftServerShellName);
|
||||
}
|
||||
|
||||
public int getStatus() {
|
||||
final SystemResult shellResult = systemService.executeCommand(buildMinecraftServerShellPath(), "api_status");
|
||||
return Integer.parseInt(shellResult.getStdOut());
|
||||
}
|
||||
|
||||
private void startOrStopServer(final HttpServletResponse pResponse, final String pAction) throws IOException {
|
||||
final SystemResult shellResult = systemService.executeCommand(buildMinecraftServerShellPath(), pAction);
|
||||
|
||||
if(shellResult.getResultCode() == ResultCode.FAILED.val()) {
|
||||
pResponse.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
|
||||
} else if(shellResult.getResultCode() == ResultCode.STATE_UNCHANGED.val()) {
|
||||
pResponse.sendError(HttpServletResponse.SC_CONFLICT);
|
||||
} // else -> SUCCESS, so code 200
|
||||
}
|
||||
|
||||
public void startServer(final HttpServletResponse pResponse) throws IOException {
|
||||
startOrStopServer(pResponse, "api_start");
|
||||
}
|
||||
|
||||
public void stopServer(final HttpServletResponse pResponse) throws IOException {
|
||||
startOrStopServer(pResponse, "api_stop");
|
||||
}
|
||||
|
||||
public void restartServer(final HttpServletResponse pResponse) throws IOException {
|
||||
startOrStopServer(pResponse, "api_restart");
|
||||
}
|
||||
}
|
||||
15
src/main/resources/application.properties
Normal file
15
src/main/resources/application.properties
Normal file
@@ -0,0 +1,15 @@
|
||||
# ***********************************************
|
||||
# Hibernate database configuration
|
||||
# ***********************************************
|
||||
spring.datasource.driverClassName=org.postgresql.Driver
|
||||
spring.datasource.url=jdbc:postgresql://localhost:5432/db_minager
|
||||
spring.datasource.username=minager
|
||||
spring.datasource.password=P@ssword
|
||||
# Disable feature detection by this undocumented parameter. Check the org.hibernate.engine.jdbc.internal.JdbcServiceImpl.configure method for more details.
|
||||
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults = false
|
||||
|
||||
# ***********************************************
|
||||
# Application custom parameters
|
||||
# ***********************************************
|
||||
minecraft.server.path=/home/minecraft/server
|
||||
minecraft.server.shell.name=minecraft-server.sh
|
||||
25
src/main/sql/0_init_db.sql
Normal file
25
src/main/sql/0_init_db.sql
Normal file
@@ -0,0 +1,25 @@
|
||||
-- Execute with psql as postgres user
|
||||
create user minager_admin with password 'P@ssword';
|
||||
create user minager with password 'P@ssword';
|
||||
create database db_minager owner minager_admin;
|
||||
\c db_minager;
|
||||
|
||||
-- ******************************************************************
|
||||
-- T A B L E S
|
||||
-- ******************************************************************
|
||||
drop table if exists "user";
|
||||
|
||||
create table "user" (
|
||||
"id" serial,
|
||||
"key" varchar,
|
||||
"name" varchar,
|
||||
"email" varchar,
|
||||
"password" varchar,
|
||||
"image" varchar,
|
||||
"inscription_date" timestamp,
|
||||
constraint user_pk primary key ("id"),
|
||||
constraint user_key_unique unique ("key"),
|
||||
constraint user_email_unique unique ("email")
|
||||
);
|
||||
grant select, insert, update, delete on "user" to minager;
|
||||
grant usage, select, update on user_id_seq to minager;
|
||||
13
src/main/ts/.editorconfig
Normal file
13
src/main/ts/.editorconfig
Normal file
@@ -0,0 +1,13 @@
|
||||
# Editor configuration, see http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
27
src/main/ts/README.md
Normal file
27
src/main/ts/README.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Ts
|
||||
|
||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 6.2.3.
|
||||
|
||||
## Development server
|
||||
|
||||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Running end-to-end tests
|
||||
|
||||
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
||||
137
src/main/ts/angular.json
Normal file
137
src/main/ts/angular.json
Normal file
@@ -0,0 +1,137 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"ts": {
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"projectType": "application",
|
||||
"prefix": "app",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"styleext": "scss"
|
||||
}
|
||||
},
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "dist/ts",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "src/tsconfig.app.json",
|
||||
"assets": [
|
||||
"src/favicon.png",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"node_modules/font-awesome/scss/font-awesome.scss",
|
||||
"node_modules/angular-bootstrap-md/scss/bootstrap/bootstrap.scss",
|
||||
"node_modules/angular-bootstrap-md/scss/mdb-free.scss",
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": [
|
||||
"node_modules/chart.js/dist/Chart.js",
|
||||
"node_modules/hammerjs/hammer.min.js"
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"aot": true,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "ts:build"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "ts:build:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "ts:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "src/test.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "src/tsconfig.spec.json",
|
||||
"karmaConfig": "src/karma.conf.js",
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": [],
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"src/tsconfig.app.json",
|
||||
"src/tsconfig.spec.json"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ts-e2e": {
|
||||
"root": "e2e/",
|
||||
"projectType": "application",
|
||||
"architect": {
|
||||
"e2e": {
|
||||
"builder": "@angular-devkit/build-angular:protractor",
|
||||
"options": {
|
||||
"protractorConfig": "e2e/protractor.conf.js",
|
||||
"devServerTarget": "ts:serve"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"devServerTarget": "ts:serve:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": "e2e/tsconfig.e2e.json",
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultProject": "ts"
|
||||
}
|
||||
28
src/main/ts/e2e/protractor.conf.js
Normal file
28
src/main/ts/e2e/protractor.conf.js
Normal file
@@ -0,0 +1,28 @@
|
||||
// Protractor configuration file, see link for more information
|
||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||
|
||||
const { SpecReporter } = require('jasmine-spec-reporter');
|
||||
|
||||
exports.config = {
|
||||
allScriptsTimeout: 11000,
|
||||
specs: [
|
||||
'./src/**/*.e2e-spec.ts'
|
||||
],
|
||||
capabilities: {
|
||||
'browserName': 'chrome'
|
||||
},
|
||||
directConnect: true,
|
||||
baseUrl: 'http://localhost:4200/',
|
||||
framework: 'jasmine',
|
||||
jasmineNodeOpts: {
|
||||
showColors: true,
|
||||
defaultTimeoutInterval: 30000,
|
||||
print: function() {}
|
||||
},
|
||||
onPrepare() {
|
||||
require('ts-node').register({
|
||||
project: require('path').join(__dirname, './tsconfig.e2e.json')
|
||||
});
|
||||
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
||||
}
|
||||
};
|
||||
14
src/main/ts/e2e/src/app.e2e-spec.ts
Normal file
14
src/main/ts/e2e/src/app.e2e-spec.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { AppPage } from './app.po';
|
||||
|
||||
describe('workspace-project App', () => {
|
||||
let page: AppPage;
|
||||
|
||||
beforeEach(() => {
|
||||
page = new AppPage();
|
||||
});
|
||||
|
||||
it('should display welcome message', () => {
|
||||
page.navigateTo();
|
||||
expect(page.getParagraphText()).toEqual('Welcome to ts!');
|
||||
});
|
||||
});
|
||||
11
src/main/ts/e2e/src/app.po.ts
Normal file
11
src/main/ts/e2e/src/app.po.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { browser, by, element } from 'protractor';
|
||||
|
||||
export class AppPage {
|
||||
navigateTo() {
|
||||
return browser.get('/');
|
||||
}
|
||||
|
||||
getParagraphText() {
|
||||
return element(by.css('app-root h1')).getText();
|
||||
}
|
||||
}
|
||||
13
src/main/ts/e2e/tsconfig.e2e.json
Normal file
13
src/main/ts/e2e/tsconfig.e2e.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/app",
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"jasminewd2",
|
||||
"node"
|
||||
]
|
||||
}
|
||||
}
|
||||
55
src/main/ts/package.json
Normal file
55
src/main/ts/package.json
Normal file
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"name": "ts",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"test": "ng test",
|
||||
"lint": "ng lint",
|
||||
"e2e": "ng e2e"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^6.1.0",
|
||||
"@angular/common": "^6.1.0",
|
||||
"@angular/compiler": "^6.1.0",
|
||||
"@angular/core": "^6.1.0",
|
||||
"@angular/forms": "^6.1.0",
|
||||
"@angular/http": "^6.1.0",
|
||||
"@angular/platform-browser": "^6.1.0",
|
||||
"@angular/platform-browser-dynamic": "^6.1.0",
|
||||
"@angular/router": "^6.1.0",
|
||||
"@ngtools/webpack": "^1.2.4",
|
||||
"@types/chart.js": "^2.7.36",
|
||||
"angular-bootstrap-md": "^6.2.4",
|
||||
"angular5-csv": "^0.2.10",
|
||||
"chart.js": "^2.5.0",
|
||||
"core-js": "^2.5.4",
|
||||
"font-awesome": "^4.7.0",
|
||||
"hammerjs": "^2.0.8",
|
||||
"rxjs": "~6.2.0",
|
||||
"zone.js": "~0.8.26"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.8.0",
|
||||
"@angular/cli": "~6.2.3",
|
||||
"@angular/compiler-cli": "^6.1.0",
|
||||
"@angular/language-service": "^6.1.0",
|
||||
"@types/jasmine": "~2.8.8",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/node": "~8.9.4",
|
||||
"codelyzer": "~4.3.0",
|
||||
"jasmine-core": "~2.99.1",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"karma": "~3.0.0",
|
||||
"karma-chrome-launcher": "~2.2.0",
|
||||
"karma-coverage-istanbul-reporter": "~2.0.1",
|
||||
"karma-jasmine": "~1.1.2",
|
||||
"karma-jasmine-html-reporter": "^0.2.2",
|
||||
"protractor": "~5.4.0",
|
||||
"ts-node": "~7.0.0",
|
||||
"tslint": "~5.11.0",
|
||||
"typescript": "~2.9.2"
|
||||
}
|
||||
}
|
||||
6
src/main/ts/src/app/app.component.html
Normal file
6
src/main/ts/src/app/app.component.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<app-header></app-header>
|
||||
<main class="container">
|
||||
<router-outlet></router-outlet>
|
||||
</main>
|
||||
<!-- <app-footer></app-footer> -->
|
||||
|
||||
4
src/main/ts/src/app/app.component.scss
Normal file
4
src/main/ts/src/app/app.component.scss
Normal file
@@ -0,0 +1,4 @@
|
||||
main {
|
||||
margin-top: 84px;
|
||||
padding-top: 25px;
|
||||
}
|
||||
27
src/main/ts/src/app/app.component.spec.ts
Normal file
27
src/main/ts/src/app/app.component.spec.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { TestBed, async } from '@angular/core/testing';
|
||||
import { AppComponent } from './app.component';
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
}).compileComponents();
|
||||
}));
|
||||
it('should create the app', async(() => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
}));
|
||||
it(`should have as title 'ts'`, async(() => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app.title).toEqual('ts');
|
||||
}));
|
||||
it('should render title in a h1 tag', async(() => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.debugElement.nativeElement;
|
||||
expect(compiled.querySelector('h1').textContent).toContain('Welcome to ts!');
|
||||
}));
|
||||
});
|
||||
10
src/main/ts/src/app/app.component.ts
Normal file
10
src/main/ts/src/app/app.component.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss']
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'app';
|
||||
}
|
||||
68
src/main/ts/src/app/app.module.ts
Normal file
68
src/main/ts/src/app/app.module.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
// **********************************************
|
||||
// Angular Core
|
||||
// **********************************************
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
|
||||
// **********************************************
|
||||
// External dependencies
|
||||
// **********************************************
|
||||
import { MDBBootstrapModule } from 'angular-bootstrap-md';
|
||||
|
||||
// **********************************************
|
||||
// App Components
|
||||
// **********************************************
|
||||
import { AppComponent } from './app.component';
|
||||
import { HeaderComponent } from './header/header.component';
|
||||
import { ServerComponent } from './server/server.component';
|
||||
import { LoginComponent } from './login/login.component';
|
||||
import { DisconnectionComponent } from './disconnection/disconnection.component';
|
||||
|
||||
// **********************************************
|
||||
// App Services
|
||||
// **********************************************
|
||||
import { ServerService } from './server/server.service';
|
||||
import { LoginService } from './login/login.service';
|
||||
import { AuthService } from './core/services/auth.service';
|
||||
|
||||
// Router
|
||||
import { appRoutes } from './app.routes';
|
||||
|
||||
// Interceptor
|
||||
import { TokenInterceptor } from './core/interceptors/token-interceptor';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
HeaderComponent,
|
||||
ServerComponent,
|
||||
LoginComponent,
|
||||
DisconnectionComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
HttpClientModule,
|
||||
FormsModule,
|
||||
RouterModule.forRoot(
|
||||
appRoutes,
|
||||
// { enableTracing: true } // Enabling tracing
|
||||
{ onSameUrlNavigation: 'reload' }
|
||||
),
|
||||
MDBBootstrapModule.forRoot()
|
||||
],
|
||||
providers: [
|
||||
ServerService,
|
||||
LoginService,
|
||||
AuthService,
|
||||
{
|
||||
provide: HTTP_INTERCEPTORS,
|
||||
useClass: TokenInterceptor,
|
||||
multi: true
|
||||
}
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule { }
|
||||
15
src/main/ts/src/app/app.routes.ts
Normal file
15
src/main/ts/src/app/app.routes.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
import { LoginComponent } from './login/login.component';
|
||||
import { DisconnectionComponent } from './disconnection/disconnection.component';
|
||||
import { ServerComponent } from './server/server.component';
|
||||
|
||||
// import { AuthGuard } from './core/guards/auth.guard';
|
||||
|
||||
export const appRoutes: Routes = [
|
||||
{ path: 'login', component: LoginComponent },
|
||||
{ path: 'disconnection', component: DisconnectionComponent },
|
||||
{ path: 'server', component: ServerComponent },
|
||||
{ path: '', redirectTo: '/server', pathMatch: 'full' },
|
||||
{ path: '**', redirectTo: '/server', pathMatch: 'full' }
|
||||
];
|
||||
10
src/main/ts/src/app/core/entities.ts
Normal file
10
src/main/ts/src/app/core/entities.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export class User {
|
||||
constructor(
|
||||
public key: string,
|
||||
public name: string,
|
||||
public email: string,
|
||||
public password: string,
|
||||
public inscriptionDate: Date,
|
||||
public token: string
|
||||
) { }
|
||||
}
|
||||
45
src/main/ts/src/app/core/interceptors/token-interceptor.ts
Normal file
45
src/main/ts/src/app/core/interceptors/token-interceptor.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, filter, tap } from 'rxjs/operators';
|
||||
|
||||
import { User } from '../entities';
|
||||
import { AuthService } from '../services/auth.service';
|
||||
|
||||
@Injectable()
|
||||
export class TokenInterceptor implements HttpInterceptor {
|
||||
|
||||
constructor(
|
||||
private authService: AuthService,
|
||||
private router: Router
|
||||
) {}
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
const token = this.authService.getToken();
|
||||
|
||||
let request: HttpRequest<any> = req;
|
||||
|
||||
if (token) {
|
||||
request = req.clone({
|
||||
setHeaders: {
|
||||
token: token
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return next.handle(request).pipe(
|
||||
tap(event => {
|
||||
// Do nothing for the interceptor
|
||||
}, (err: any) => {
|
||||
if (err instanceof HttpErrorResponse && err.status === 401) {
|
||||
this.authService.disconnect();
|
||||
this.router.navigate(['/login']);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
39
src/main/ts/src/app/core/services/auth.service.ts
Normal file
39
src/main/ts/src/app/core/services/auth.service.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { User } from '../entities';
|
||||
|
||||
const PARAM_TOKEN = 'token';
|
||||
const PARAM_USER = 'user';
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
|
||||
constructor() {}
|
||||
|
||||
public getToken(): string {
|
||||
return localStorage.getItem(PARAM_TOKEN);
|
||||
}
|
||||
|
||||
public setToken(token: string): void {
|
||||
localStorage.setItem(PARAM_TOKEN, token);
|
||||
}
|
||||
|
||||
public isAuthenticated(): boolean {
|
||||
return this.getToken() != null;
|
||||
}
|
||||
|
||||
public disconnect(): void {
|
||||
localStorage.clear();
|
||||
}
|
||||
|
||||
public isAdmin(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
public setUser(user: User): void {
|
||||
localStorage.setItem(PARAM_USER, JSON.stringify(user));
|
||||
}
|
||||
|
||||
public getUser(): User {
|
||||
return JSON.parse(localStorage.getItem(PARAM_USER));
|
||||
}
|
||||
}
|
||||
21
src/main/ts/src/app/disconnection/disconnection.component.ts
Normal file
21
src/main/ts/src/app/disconnection/disconnection.component.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { AuthService } from '../core/services/auth.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-disconnection',
|
||||
template: 'Déconnexion...'
|
||||
})
|
||||
export class DisconnectionComponent implements OnInit {
|
||||
|
||||
constructor(
|
||||
private authService: AuthService,
|
||||
private router: Router
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.authService.disconnect();
|
||||
this.router.navigate(['/server']);
|
||||
}
|
||||
}
|
||||
27
src/main/ts/src/app/header/header.component.html
Normal file
27
src/main/ts/src/app/header/header.component.html
Normal file
@@ -0,0 +1,27 @@
|
||||
<mdb-navbar SideClass="navbar fixed-top navbar-expand-lg navbar-dark green scrolling-navbar ie-nav" [containerInside]="false">
|
||||
<logo>
|
||||
<a class="logo navbar-brand" routerLink="/">
|
||||
<img id="logo" src="./assets/favicon.png" alt="logo" />
|
||||
<span id="title" *ngIf="!title">Minager</span>
|
||||
<span id="title" *ngIf="title">Minager - {{title}}</span>
|
||||
</a>
|
||||
</logo>
|
||||
<links>
|
||||
<ul class="navbar-nav ml-auto nav-flex-icons" style="margin-left: 0 !important;">
|
||||
<li class="nav-item">
|
||||
<a routerLink="/login"
|
||||
*ngIf="!isAuthenticated()"
|
||||
class="nav-link waves-light"
|
||||
mdbRippleRadius>
|
||||
<i class="fa fa-sign-in"></i> Connexion
|
||||
</a>
|
||||
<a routerLink="/disconnection"
|
||||
*ngIf="isAuthenticated()"
|
||||
class="nav-link waves-light danger-color-dark"
|
||||
mdbRippleRadius >
|
||||
<i class="fa fa-sign-out"></i> Déconnexion
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</links>
|
||||
</mdb-navbar>
|
||||
22
src/main/ts/src/app/header/header.component.ts
Normal file
22
src/main/ts/src/app/header/header.component.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { AuthService } from '../core/services/auth.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
templateUrl: './header.component.html',
|
||||
styles: [`
|
||||
img {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
`]
|
||||
})
|
||||
export class HeaderComponent {
|
||||
constructor(
|
||||
private authService: AuthService
|
||||
) {}
|
||||
|
||||
isAuthenticated(): boolean {
|
||||
return this.authService.isAuthenticated();
|
||||
}
|
||||
}
|
||||
22
src/main/ts/src/app/login/login.component.html
Normal file
22
src/main/ts/src/app/login/login.component.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<div>
|
||||
<label for="email">Adresse mail</label>
|
||||
<input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
[(ngModel)]="model.email" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="password">Mot de passe</label>
|
||||
<input
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
[(ngModel)]="model.password" />
|
||||
</div>
|
||||
<button (click)="submitLogin()">Suivant</button>
|
||||
<div id="errorMsg" class="card red lighten-2 text-center z-depth-2">
|
||||
<div class="card-body">
|
||||
<p class="white-text mb-0">{{loginError}}</p>
|
||||
</div>
|
||||
</div>
|
||||
60
src/main/ts/src/app/login/login.component.ts
Normal file
60
src/main/ts/src/app/login/login.component.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { User } from '../core/entities';
|
||||
import { LoginService } from './login.service';
|
||||
import { AuthService } from '../core/services/auth.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-login',
|
||||
templateUrl: 'login.component.html',
|
||||
styles: [`
|
||||
#form {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.submitFormArea {
|
||||
line-height: 50px;
|
||||
}
|
||||
|
||||
#errorMsg {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.5s ease-out;
|
||||
margin: 0;
|
||||
}
|
||||
`]
|
||||
})
|
||||
export class LoginComponent {
|
||||
model: User = new User('', '', '', '', undefined, '');
|
||||
loginError;
|
||||
|
||||
constructor(
|
||||
private loginService: LoginService,
|
||||
private authService: AuthService,
|
||||
private router: Router
|
||||
) {}
|
||||
|
||||
submitLogin(): void {
|
||||
this.loginService.login(this.model).subscribe(user => {
|
||||
this.authService.setToken(user.token);
|
||||
this.authService.setUser(user);
|
||||
this.router.navigate(['/server']);
|
||||
}, error => {
|
||||
this.setMessage('Email ou password incorrect.');
|
||||
});
|
||||
}
|
||||
|
||||
setMessage(message: string): void {
|
||||
this.loginError = message;
|
||||
|
||||
const resultMsgDiv = document.getElementById('errorMsg');
|
||||
resultMsgDiv.style.maxHeight = '64px';
|
||||
|
||||
setTimeout(() => {
|
||||
resultMsgDiv.style.maxHeight = '0px';
|
||||
setTimeout(() => {
|
||||
this.loginError = undefined;
|
||||
}, 550);
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
22
src/main/ts/src/app/login/login.service.ts
Normal file
22
src/main/ts/src/app/login/login.service.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
import { User } from '../core/entities';
|
||||
|
||||
const LOGIN_URL = environment.apiUrl + '/api/account/login';
|
||||
|
||||
@Injectable()
|
||||
export class LoginService {
|
||||
|
||||
constructor(
|
||||
private http: HttpClient
|
||||
) {}
|
||||
|
||||
login(user: User): Observable<User> {
|
||||
return this.http.post<User>(LOGIN_URL, user);
|
||||
}
|
||||
}
|
||||
46
src/main/ts/src/app/server/server.component.html
Normal file
46
src/main/ts/src/app/server/server.component.html
Normal file
@@ -0,0 +1,46 @@
|
||||
<div>
|
||||
Status du serveur :
|
||||
<span *ngIf="!serverStartedChecked" class="badge badge-warning">
|
||||
Vérification
|
||||
</span>
|
||||
<span *ngIf="serverStartedChecked" class="badge"
|
||||
[ngClass]="serverStarted ? 'badge-success' : 'badge-danger'">
|
||||
{{serverStarted ? 'Démarré' : 'Éteint'}}
|
||||
</span>
|
||||
</div>
|
||||
<div *ngIf="isAuthenticated()">
|
||||
<button mdbBtn
|
||||
*ngIf="serverStarted"
|
||||
type="button"
|
||||
color="primary"
|
||||
class="waves-light"
|
||||
[disabled]="restarting"
|
||||
(click)="restartServer()"
|
||||
mdbWavesEffect>
|
||||
Redémarrer
|
||||
</button>
|
||||
<button mdbBtn
|
||||
type="button"
|
||||
class="waves-light"
|
||||
[ngClass]="serverStarted ? 'btn-danger' : 'btn-success'"
|
||||
[disabled]="restarting"
|
||||
(click)="startOrStopServer()"
|
||||
mdbWavesEffect>
|
||||
{{serverStarted ? 'Éteindre' : 'Démarrer'}}
|
||||
</button>
|
||||
</div>
|
||||
<div id="errorMsg" class="msg card red lighten-2 text-center z-depth-2">
|
||||
<div class="card-body">
|
||||
<p class="white-text mb-0">{{errorMsg}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="warnMsg" class="msg card orange lighten-2 text-center z-depth-2">
|
||||
<div class="card-body">
|
||||
<p class="white-text mb-0">{{warnMsg}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="successMsg" class="msg card green lighten-2 text-center z-depth-2">
|
||||
<div class="card-body">
|
||||
<p class="white-text mb-0">{{successMsg}}</p>
|
||||
</div>
|
||||
</div>
|
||||
85
src/main/ts/src/app/server/server.component.ts
Normal file
85
src/main/ts/src/app/server/server.component.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ServerService } from './server.service';
|
||||
import { AuthService } from '../core/services/auth.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-server',
|
||||
templateUrl: 'server.component.html',
|
||||
styles: [`
|
||||
.msg {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.5s ease-out;
|
||||
margin: 0;
|
||||
}
|
||||
`]
|
||||
})
|
||||
export class ServerComponent implements OnInit {
|
||||
serverStartedChecked = false;
|
||||
serverStarted = false;
|
||||
errorMsg;
|
||||
warnMsg;
|
||||
successMsg;
|
||||
restarting = false;
|
||||
|
||||
constructor(
|
||||
private serverService: ServerService,
|
||||
private authService: AuthService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.serverService.getStatus().subscribe(pServerStatus => {
|
||||
this.serverStartedChecked = true;
|
||||
this.serverStarted = pServerStatus === 1;
|
||||
});
|
||||
}
|
||||
|
||||
isAuthenticated(): boolean {
|
||||
return this.authService.isAuthenticated();
|
||||
}
|
||||
|
||||
restartServer(): void {
|
||||
this.restarting = true;
|
||||
|
||||
this.serverService.restartServer().subscribe(() => {
|
||||
this.setMessage('Serveur redémarré.', 'successMsg');
|
||||
}, error => {
|
||||
this.setMessage('Une erreur est survenue lors du redémarrage du serveur.',
|
||||
'errorMsg');
|
||||
}, () => {
|
||||
this.restarting = false;
|
||||
});
|
||||
}
|
||||
|
||||
startOrStopServer(): void {
|
||||
this.serverService[(this.serverStarted ? 'stop' : 'start') + 'Server']().subscribe(() => {
|
||||
this.setMessage('Serveur ' + (this.serverStarted ? 'éteint' : 'démarré') + '.', 'successMsg');
|
||||
this.serverStarted = !this.serverStarted;
|
||||
}, error => {
|
||||
if (error.status === 409) {
|
||||
this.setMessage('Le serveur est déjà '
|
||||
+ (this.serverStarted ? 'éteint' : 'démarré') + '.',
|
||||
'warnMsg');
|
||||
} else {
|
||||
this.setMessage('Une erreur est survenue lors '
|
||||
+ (this.serverStarted ? 'de l\'extinction' : 'du démarrage')
|
||||
+ 'du serveur.',
|
||||
'errorMsg');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setMessage(message: string, type: string): void {
|
||||
this[type] = message;
|
||||
|
||||
const resultMsgDiv = document.getElementById(type);
|
||||
resultMsgDiv.style.maxHeight = '64px';
|
||||
|
||||
setTimeout(() => {
|
||||
resultMsgDiv.style.maxHeight = '0px';
|
||||
setTimeout(() => {
|
||||
this[type] = undefined;
|
||||
}, 550);
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
29
src/main/ts/src/app/server/server.service.ts
Normal file
29
src/main/ts/src/app/server/server.service.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
const SERVER_URL = environment.apiUrl + '/api/server';
|
||||
|
||||
@Injectable()
|
||||
export class ServerService {
|
||||
constructor(
|
||||
private http: HttpClient
|
||||
) {}
|
||||
|
||||
getStatus(): Observable<number> {
|
||||
return this.http.get<number>(`${SERVER_URL}/status`);
|
||||
}
|
||||
|
||||
startServer(): Observable<any> {
|
||||
return this.http.post(`${SERVER_URL}/start`, undefined);
|
||||
}
|
||||
|
||||
stopServer(): Observable<any> {
|
||||
return this.http.post(`${SERVER_URL}/stop`, undefined);
|
||||
}
|
||||
|
||||
restartServer(): Observable<any> {
|
||||
return this.http.post(`${SERVER_URL}/restart`, undefined);
|
||||
}
|
||||
}
|
||||
BIN
src/main/ts/src/assets/favicon.png
Normal file
BIN
src/main/ts/src/assets/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 107 KiB |
11
src/main/ts/src/browserslist
Normal file
11
src/main/ts/src/browserslist
Normal file
@@ -0,0 +1,11 @@
|
||||
# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
#
|
||||
# For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed
|
||||
|
||||
> 0.5%
|
||||
last 2 versions
|
||||
Firefox ESR
|
||||
not dead
|
||||
not IE 9-11
|
||||
3
src/main/ts/src/environments/environment.prod.ts
Normal file
3
src/main/ts/src/environments/environment.prod.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const environment = {
|
||||
production: true
|
||||
};
|
||||
19
src/main/ts/src/environments/environment.ts
Normal file
19
src/main/ts/src/environments/environment.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
// This file can be replaced during build by using the `fileReplacements` array.
|
||||
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
|
||||
// The list of file replacements can be found in `angular.json`.
|
||||
|
||||
export const environment = {
|
||||
production: false,
|
||||
apiUrl: 'http://localhost:8080',
|
||||
appVersion: '0.0.1',
|
||||
title: 'LOCAL'
|
||||
};
|
||||
|
||||
/*
|
||||
* For easier debugging in development mode, you can import the following file
|
||||
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
|
||||
*
|
||||
* This import should be commented out in production mode because it will have a negative impact
|
||||
* on performance if an error is thrown.
|
||||
*/
|
||||
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
|
||||
BIN
src/main/ts/src/favicon.ico
Normal file
BIN
src/main/ts/src/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.3 KiB |
BIN
src/main/ts/src/favicon.png
Normal file
BIN
src/main/ts/src/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 107 KiB |
14
src/main/ts/src/index.html
Normal file
14
src/main/ts/src/index.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<!doctype html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<title>Minager</title>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<base href="/" />
|
||||
<link rel="icon" type="image/x-icon" href="favicon.png">
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
||||
31
src/main/ts/src/karma.conf.js
Normal file
31
src/main/ts/src/karma.conf.js
Normal file
@@ -0,0 +1,31 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage-istanbul-reporter'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
client: {
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, '../coverage'),
|
||||
reports: ['html', 'lcovonly'],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false
|
||||
});
|
||||
};
|
||||
13
src/main/ts/src/main.ts
Normal file
13
src/main/ts/src/main.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
import { environment } from './environments/environment';
|
||||
|
||||
if (environment.production) {
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||
.catch(err => console.error(err));
|
||||
|
||||
80
src/main/ts/src/polyfills.ts
Normal file
80
src/main/ts/src/polyfills.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* This file includes polyfills needed by Angular and is loaded before the app.
|
||||
* You can add your own extra polyfills to this file.
|
||||
*
|
||||
* This file is divided into 2 sections:
|
||||
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
|
||||
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
|
||||
* file.
|
||||
*
|
||||
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
|
||||
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
|
||||
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
|
||||
*
|
||||
* Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
|
||||
*/
|
||||
|
||||
/***************************************************************************************************
|
||||
* BROWSER POLYFILLS
|
||||
*/
|
||||
|
||||
/** IE9, IE10 and IE11 requires all of the following polyfills. **/
|
||||
// import 'core-js/es6/symbol';
|
||||
// import 'core-js/es6/object';
|
||||
// import 'core-js/es6/function';
|
||||
// import 'core-js/es6/parse-int';
|
||||
// import 'core-js/es6/parse-float';
|
||||
// import 'core-js/es6/number';
|
||||
// import 'core-js/es6/math';
|
||||
// import 'core-js/es6/string';
|
||||
// import 'core-js/es6/date';
|
||||
// import 'core-js/es6/array';
|
||||
// import 'core-js/es6/regexp';
|
||||
// import 'core-js/es6/map';
|
||||
// import 'core-js/es6/weak-map';
|
||||
// import 'core-js/es6/set';
|
||||
|
||||
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
|
||||
// import 'classlist.js'; // Run `npm install --save classlist.js`.
|
||||
|
||||
/** IE10 and IE11 requires the following for the Reflect API. */
|
||||
// import 'core-js/es6/reflect';
|
||||
|
||||
|
||||
/** Evergreen browsers require these. **/
|
||||
// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
|
||||
import 'core-js/es7/reflect';
|
||||
|
||||
|
||||
/**
|
||||
* Web Animations `@angular/platform-browser/animations`
|
||||
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
|
||||
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
|
||||
**/
|
||||
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
||||
|
||||
/**
|
||||
* By default, zone.js will patch all possible macroTask and DomEvents
|
||||
* user can disable parts of macroTask/DomEvents patch by setting following flags
|
||||
*/
|
||||
|
||||
// (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
|
||||
// (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
|
||||
// (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
|
||||
|
||||
/*
|
||||
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
|
||||
* with the following flag, it will bypass `zone.js` patch for IE/Edge
|
||||
*/
|
||||
// (window as any).__Zone_enable_cross_context_check = true;
|
||||
|
||||
/***************************************************************************************************
|
||||
* Zone JS is required by default for Angular itself.
|
||||
*/
|
||||
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
||||
|
||||
|
||||
|
||||
/***************************************************************************************************
|
||||
* APPLICATION IMPORTS
|
||||
*/
|
||||
1
src/main/ts/src/styles.scss
Normal file
1
src/main/ts/src/styles.scss
Normal file
@@ -0,0 +1 @@
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
20
src/main/ts/src/test.ts
Normal file
20
src/main/ts/src/test.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||
|
||||
import 'zone.js/dist/zone-testing';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
import {
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting
|
||||
} from '@angular/platform-browser-dynamic/testing';
|
||||
|
||||
declare const require: any;
|
||||
|
||||
// First, initialize the Angular testing environment.
|
||||
getTestBed().initTestEnvironment(
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting()
|
||||
);
|
||||
// Then we find all the tests.
|
||||
const context = require.context('./', true, /\.spec\.ts$/);
|
||||
// And load the modules.
|
||||
context.keys().map(context);
|
||||
11
src/main/ts/src/tsconfig.app.json
Normal file
11
src/main/ts/src/tsconfig.app.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/app",
|
||||
"types": []
|
||||
},
|
||||
"exclude": [
|
||||
"test.ts",
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
18
src/main/ts/src/tsconfig.spec.json
Normal file
18
src/main/ts/src/tsconfig.spec.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"test.ts",
|
||||
"polyfills.ts"
|
||||
],
|
||||
"include": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.d.ts"
|
||||
]
|
||||
}
|
||||
17
src/main/ts/src/tslint.json
Normal file
17
src/main/ts/src/tslint.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "../tslint.json",
|
||||
"rules": {
|
||||
"directive-selector": [
|
||||
true,
|
||||
"attribute",
|
||||
"app",
|
||||
"camelCase"
|
||||
],
|
||||
"component-selector": [
|
||||
true,
|
||||
"element",
|
||||
"app",
|
||||
"kebab-case"
|
||||
]
|
||||
}
|
||||
}
|
||||
21
src/main/ts/tsconfig.json
Normal file
21
src/main/ts/tsconfig.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"outDir": "./dist/out-tsc",
|
||||
"sourceMap": true,
|
||||
"declaration": false,
|
||||
"module": "es2015",
|
||||
"moduleResolution": "node",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"target": "es5",
|
||||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
],
|
||||
"lib": [
|
||||
"es2017",
|
||||
"dom"
|
||||
]
|
||||
}
|
||||
}
|
||||
131
src/main/ts/tslint.json
Normal file
131
src/main/ts/tslint.json
Normal file
@@ -0,0 +1,131 @@
|
||||
{
|
||||
"rulesDirectory": [
|
||||
"node_modules/codelyzer"
|
||||
],
|
||||
"rules": {
|
||||
"arrow-return-shorthand": true,
|
||||
"callable-types": true,
|
||||
"class-name": true,
|
||||
"comment-format": [
|
||||
true,
|
||||
"check-space"
|
||||
],
|
||||
"curly": true,
|
||||
"deprecation": {
|
||||
"severity": "warn"
|
||||
},
|
||||
"eofline": true,
|
||||
"forin": true,
|
||||
"import-blacklist": [
|
||||
true,
|
||||
"rxjs/Rx"
|
||||
],
|
||||
"import-spacing": true,
|
||||
"indent": [
|
||||
true,
|
||||
"spaces"
|
||||
],
|
||||
"interface-over-type-literal": true,
|
||||
"label-position": true,
|
||||
"max-line-length": [
|
||||
true,
|
||||
140
|
||||
],
|
||||
"member-access": false,
|
||||
"member-ordering": [
|
||||
true,
|
||||
{
|
||||
"order": [
|
||||
"static-field",
|
||||
"instance-field",
|
||||
"static-method",
|
||||
"instance-method"
|
||||
]
|
||||
}
|
||||
],
|
||||
"no-arg": true,
|
||||
"no-bitwise": true,
|
||||
"no-console": [
|
||||
true,
|
||||
"debug",
|
||||
"info",
|
||||
"time",
|
||||
"timeEnd",
|
||||
"trace"
|
||||
],
|
||||
"no-construct": true,
|
||||
"no-debugger": true,
|
||||
"no-duplicate-super": true,
|
||||
"no-empty": false,
|
||||
"no-empty-interface": true,
|
||||
"no-eval": true,
|
||||
"no-inferrable-types": [
|
||||
true,
|
||||
"ignore-params"
|
||||
],
|
||||
"no-misused-new": true,
|
||||
"no-non-null-assertion": true,
|
||||
"no-redundant-jsdoc": true,
|
||||
"no-shadowed-variable": true,
|
||||
"no-string-literal": false,
|
||||
"no-string-throw": true,
|
||||
"no-switch-case-fall-through": true,
|
||||
"no-trailing-whitespace": true,
|
||||
"no-unnecessary-initializer": true,
|
||||
"no-unused-expression": true,
|
||||
"no-use-before-declare": true,
|
||||
"no-var-keyword": true,
|
||||
"object-literal-sort-keys": false,
|
||||
"one-line": [
|
||||
true,
|
||||
"check-open-brace",
|
||||
"check-catch",
|
||||
"check-else",
|
||||
"check-whitespace"
|
||||
],
|
||||
"prefer-const": true,
|
||||
"quotemark": [
|
||||
true,
|
||||
"single"
|
||||
],
|
||||
"radix": true,
|
||||
"semicolon": [
|
||||
true,
|
||||
"always"
|
||||
],
|
||||
"triple-equals": [
|
||||
true,
|
||||
"allow-null-check"
|
||||
],
|
||||
"typedef-whitespace": [
|
||||
true,
|
||||
{
|
||||
"call-signature": "nospace",
|
||||
"index-signature": "nospace",
|
||||
"parameter": "nospace",
|
||||
"property-declaration": "nospace",
|
||||
"variable-declaration": "nospace"
|
||||
}
|
||||
],
|
||||
"unified-signatures": true,
|
||||
"variable-name": false,
|
||||
"whitespace": [
|
||||
true,
|
||||
"check-branch",
|
||||
"check-decl",
|
||||
"check-operator",
|
||||
"check-separator",
|
||||
"check-type"
|
||||
],
|
||||
"no-output-on-prefix": true,
|
||||
"use-input-property-decorator": true,
|
||||
"use-output-property-decorator": true,
|
||||
"use-host-property-decorator": true,
|
||||
"no-input-rename": true,
|
||||
"no-output-rename": true,
|
||||
"use-life-cycle-interface": true,
|
||||
"use-pipe-transform-interface": true,
|
||||
"component-class-suffix": true,
|
||||
"directive-class-suffix": true
|
||||
}
|
||||
}
|
||||
16
src/test/java/org/minager/MinagerApplicationTests.java
Normal file
16
src/test/java/org/minager/MinagerApplicationTests.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package org.minager;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
public class MinagerApplicationTests {
|
||||
|
||||
@Test
|
||||
public void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user