Change directory's files
@ -1,11 +0,0 @@
|
||||
# EditorConfig is awesome: https://EditorConfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
185
Back-end/.gitignore
vendored
@ -1,185 +0,0 @@
|
||||
### Vert.x ###
|
||||
.vertx/
|
||||
|
||||
### Eclipse ###
|
||||
|
||||
.metadata
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
.recommenders
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# PyDev specific (Python IDE for Eclipse)
|
||||
*.pydevproject
|
||||
|
||||
# CDT-specific (C/C++ Development Tooling)
|
||||
.cproject
|
||||
|
||||
# Java annotation processor (APT)
|
||||
.factorypath
|
||||
|
||||
# PDT-specific (PHP Development Tools)
|
||||
.buildpath
|
||||
|
||||
# sbteclipse plugin
|
||||
.target
|
||||
|
||||
# Tern plugin
|
||||
.tern-project
|
||||
|
||||
# TeXlipse plugin
|
||||
.texlipse
|
||||
|
||||
# STS (Spring Tool Suite)
|
||||
.springBeans
|
||||
|
||||
# Code Recommenders
|
||||
.recommenders/
|
||||
|
||||
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||
.cache-main
|
||||
.scala_dependencies
|
||||
.worksheet
|
||||
|
||||
### Intellij+iml ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff:
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/dictionaries
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.xml
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# CMake
|
||||
cmake-buildTool-debug/
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-buildTool.properties
|
||||
fabric.properties
|
||||
|
||||
### Intellij+iml Patch ###
|
||||
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
|
||||
|
||||
*.iml
|
||||
modules.xml
|
||||
.idea/misc.xml
|
||||
*.ipr
|
||||
|
||||
### macOS ###
|
||||
*.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### Maven ###
|
||||
target/
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
pom.xml.next
|
||||
release.properties
|
||||
dependency-reduced-pom.xml
|
||||
buildNumber.properties
|
||||
.mvn/timing.properties
|
||||
|
||||
# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)
|
||||
!/.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
### Gradle ###
|
||||
.gradle
|
||||
/buildTool/
|
||||
|
||||
# Ignore Gradle GUI config
|
||||
gradle-app.setting
|
||||
|
||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||
!gradle-wrapper.jar
|
||||
|
||||
# Cache of project
|
||||
.gradletasknamecache
|
||||
|
||||
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
|
||||
# gradle/wrapper/gradle-wrapper.properties
|
||||
|
||||
### NetBeans ###
|
||||
nbproject/private/
|
||||
buildTool/
|
||||
nbbuild/
|
||||
dist/
|
||||
nbdist/
|
||||
.nb-gradle/
|
||||
|
||||
### VisualStudioCode ###
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
BIN
Back-end/.mvn/wrapper/maven-wrapper.jar
vendored
18
Back-end/.mvn/wrapper/maven-wrapper.properties
vendored
@ -1,18 +0,0 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip
|
||||
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
|
||||
@ -1,25 +0,0 @@
|
||||
# Stage 1: Build the Maven artifact
|
||||
FROM maven:3.9-eclipse-temurin-17 AS build
|
||||
WORKDIR /app
|
||||
|
||||
# Optimize build by caching dependencies where possible
|
||||
COPY pom.xml .
|
||||
# Download dependencies
|
||||
RUN mvn dependency:go-offline -B
|
||||
|
||||
# Copy source code and build the JAR
|
||||
COPY src ./src
|
||||
RUN mvn clean package -DskipTests
|
||||
|
||||
# Stage 2: Setup the runtime environment
|
||||
FROM eclipse-temurin:17-jre
|
||||
WORKDIR /app
|
||||
|
||||
# Copy the constructed fat JAR from the builder stage
|
||||
COPY --from=build /app/target/starter-1.0.0-SNAPSHOT-fat.jar ./app.jar
|
||||
COPY keystore.jceks ./
|
||||
|
||||
EXPOSE 8888
|
||||
|
||||
# Start the Vert.x application
|
||||
ENTRYPOINT ["java", "-jar", "app.jar"]
|
||||
@ -1,31 +0,0 @@
|
||||
= Starter
|
||||
|
||||
image:https://img.shields.io/badge/vert.x-4.5.13-purple.svg[link="https://vertx.io"]
|
||||
|
||||
This application was generated using http://start.vertx.io
|
||||
|
||||
== Building
|
||||
|
||||
To launch your tests:
|
||||
```
|
||||
./mvnw clean test
|
||||
```
|
||||
|
||||
To package your application:
|
||||
```
|
||||
./mvnw clean package
|
||||
```
|
||||
|
||||
To run your application:
|
||||
```
|
||||
./mvnw clean compile exec:java
|
||||
```
|
||||
|
||||
== Help
|
||||
|
||||
* https://vertx.io/docs/[Vert.x Documentation]
|
||||
* https://stackoverflow.com/questions/tagged/vert.x?sort=newest&pageSize=15[Vert.x Stack Overflow]
|
||||
* https://groups.google.com/forum/?fromgroups#!forum/vertx[Vert.x User Group]
|
||||
* https://discord.gg/6ry7aqPWXy[Vert.x Discord]
|
||||
|
||||
|
||||
308
Back-end/mvnw
vendored
@ -1,308 +0,0 @@
|
||||
#!/bin/sh
|
||||
# ----------------------------------------------------------------------------
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Apache Maven Wrapper startup batch script, version 3.2.0
|
||||
#
|
||||
# Required ENV vars:
|
||||
# ------------------
|
||||
# JAVA_HOME - location of a JDK home dir
|
||||
#
|
||||
# Optional ENV vars
|
||||
# -----------------
|
||||
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
# e.g. to debug Maven itself, use
|
||||
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
||||
|
||||
if [ -f /usr/local/etc/mavenrc ] ; then
|
||||
. /usr/local/etc/mavenrc
|
||||
fi
|
||||
|
||||
if [ -f /etc/mavenrc ] ; then
|
||||
. /etc/mavenrc
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.mavenrc" ] ; then
|
||||
. "$HOME/.mavenrc"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# OS specific support. $var _must_ be set to either true or false.
|
||||
cygwin=false;
|
||||
darwin=false;
|
||||
mingw=false
|
||||
case "$(uname)" in
|
||||
CYGWIN*) cygwin=true ;;
|
||||
MINGW*) mingw=true;;
|
||||
Darwin*) darwin=true
|
||||
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
|
||||
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
if [ -x "/usr/libexec/java_home" ]; then
|
||||
JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
|
||||
else
|
||||
JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
if [ -r /etc/gentoo-release ] ; then
|
||||
JAVA_HOME=$(java-config --jre-home)
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||
if $cygwin ; then
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
|
||||
fi
|
||||
|
||||
# For Mingw, ensure paths are in UNIX format before anything is touched
|
||||
if $mingw ; then
|
||||
[ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
|
||||
JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
javaExecutable="$(which javac)"
|
||||
if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
|
||||
# readlink(1) is not available as standard on Solaris 10.
|
||||
readLink=$(which readlink)
|
||||
if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
|
||||
if $darwin ; then
|
||||
javaHome="$(dirname "\"$javaExecutable\"")"
|
||||
javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
|
||||
else
|
||||
javaExecutable="$(readlink -f "\"$javaExecutable\"")"
|
||||
fi
|
||||
javaHome="$(dirname "\"$javaExecutable\"")"
|
||||
javaHome=$(expr "$javaHome" : '\(.*\)/bin')
|
||||
JAVA_HOME="$javaHome"
|
||||
export JAVA_HOME
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$JAVACMD" ] ; then
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
else
|
||||
JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||
echo " We cannot execute $JAVACMD" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
echo "Warning: JAVA_HOME environment variable is not set."
|
||||
fi
|
||||
|
||||
# traverses directory structure from process work directory to filesystem root
|
||||
# first directory with .mvn subdirectory is considered project base directory
|
||||
find_maven_basedir() {
|
||||
if [ -z "$1" ]
|
||||
then
|
||||
echo "Path not specified to find_maven_basedir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
basedir="$1"
|
||||
wdir="$1"
|
||||
while [ "$wdir" != '/' ] ; do
|
||||
if [ -d "$wdir"/.mvn ] ; then
|
||||
basedir=$wdir
|
||||
break
|
||||
fi
|
||||
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
|
||||
if [ -d "${wdir}" ]; then
|
||||
wdir=$(cd "$wdir/.." || exit 1; pwd)
|
||||
fi
|
||||
# end of workaround
|
||||
done
|
||||
printf '%s' "$(cd "$basedir" || exit 1; pwd)"
|
||||
}
|
||||
|
||||
# concatenates all lines of a file
|
||||
concat_lines() {
|
||||
if [ -f "$1" ]; then
|
||||
# Remove \r in case we run on Windows within Git Bash
|
||||
# and check out the repository with auto CRLF management
|
||||
# enabled. Otherwise, we may read lines that are delimited with
|
||||
# \r\n and produce $'-Xarg\r' rather than -Xarg due to word
|
||||
# splitting rules.
|
||||
tr -s '\r\n' ' ' < "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
log() {
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
printf '%s\n' "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
|
||||
if [ -z "$BASE_DIR" ]; then
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
|
||||
log "$MAVEN_PROJECTBASEDIR"
|
||||
|
||||
##########################################################################################
|
||||
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||
# This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||
##########################################################################################
|
||||
wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
|
||||
if [ -r "$wrapperJarPath" ]; then
|
||||
log "Found $wrapperJarPath"
|
||||
else
|
||||
log "Couldn't find $wrapperJarPath, downloading it ..."
|
||||
|
||||
if [ -n "$MVNW_REPOURL" ]; then
|
||||
wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
||||
else
|
||||
wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
||||
fi
|
||||
while IFS="=" read -r key value; do
|
||||
# Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
|
||||
safeValue=$(echo "$value" | tr -d '\r')
|
||||
case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
|
||||
esac
|
||||
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
|
||||
log "Downloading from: $wrapperUrl"
|
||||
|
||||
if $cygwin; then
|
||||
wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
|
||||
fi
|
||||
|
||||
if command -v wget > /dev/null; then
|
||||
log "Found wget ... using wget"
|
||||
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||
else
|
||||
wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||
fi
|
||||
elif command -v curl > /dev/null; then
|
||||
log "Found curl ... using curl"
|
||||
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
|
||||
else
|
||||
curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
|
||||
fi
|
||||
else
|
||||
log "Falling back to using Java to download"
|
||||
javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
|
||||
javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
|
||||
# For Cygwin, switch paths to Windows format before running javac
|
||||
if $cygwin; then
|
||||
javaSource=$(cygpath --path --windows "$javaSource")
|
||||
javaClass=$(cygpath --path --windows "$javaClass")
|
||||
fi
|
||||
if [ -e "$javaSource" ]; then
|
||||
if [ ! -e "$javaClass" ]; then
|
||||
log " - Compiling MavenWrapperDownloader.java ..."
|
||||
("$JAVA_HOME/bin/javac" "$javaSource")
|
||||
fi
|
||||
if [ -e "$javaClass" ]; then
|
||||
log " - Running MavenWrapperDownloader.java ..."
|
||||
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
##########################################################################################
|
||||
# End of extension
|
||||
##########################################################################################
|
||||
|
||||
# If specified, validate the SHA-256 sum of the Maven wrapper jar file
|
||||
wrapperSha256Sum=""
|
||||
while IFS="=" read -r key value; do
|
||||
case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
|
||||
esac
|
||||
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
|
||||
if [ -n "$wrapperSha256Sum" ]; then
|
||||
wrapperSha256Result=false
|
||||
if command -v sha256sum > /dev/null; then
|
||||
if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
|
||||
wrapperSha256Result=true
|
||||
fi
|
||||
elif command -v shasum > /dev/null; then
|
||||
if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
|
||||
wrapperSha256Result=true
|
||||
fi
|
||||
else
|
||||
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
|
||||
echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
|
||||
exit 1
|
||||
fi
|
||||
if [ $wrapperSha256Result = false ]; then
|
||||
echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
|
||||
echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
|
||||
echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin; then
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
|
||||
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
|
||||
MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
|
||||
fi
|
||||
|
||||
# Provide a "standardized" way to retrieve the CLI args that will
|
||||
# work with both Windows and non-Windows executions.
|
||||
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
|
||||
export MAVEN_CMD_LINE_ARGS
|
||||
|
||||
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
# shellcheck disable=SC2086 # safe args
|
||||
exec "$JAVACMD" \
|
||||
$MAVEN_OPTS \
|
||||
$MAVEN_DEBUG_OPTS \
|
||||
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
|
||||
205
Back-end/mvnw.cmd
vendored
@ -1,205 +0,0 @@
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM http://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Apache Maven Wrapper startup batch script, version 3.2.0
|
||||
@REM
|
||||
@REM Required ENV vars:
|
||||
@REM JAVA_HOME - location of a JDK home dir
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
||||
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
|
||||
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
@REM e.g. to debug Maven itself, use
|
||||
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||
@echo off
|
||||
@REM set title of command window
|
||||
title %0
|
||||
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
|
||||
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||
|
||||
@REM set %HOME% to equivalent of $HOME
|
||||
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
||||
|
||||
@REM Execute a user defined script before this one
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
|
||||
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
|
||||
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
|
||||
:skipRcPre
|
||||
|
||||
@setlocal
|
||||
|
||||
set ERROR_CODE=0
|
||||
|
||||
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
||||
@setlocal
|
||||
|
||||
@REM ==== START VALIDATION ====
|
||||
if not "%JAVA_HOME%" == "" goto OkJHome
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME not found in your environment. >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
:OkJHome
|
||||
if exist "%JAVA_HOME%\bin\java.exe" goto init
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
||||
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
@REM ==== END VALIDATION ====
|
||||
|
||||
:init
|
||||
|
||||
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
||||
@REM Fallback to current working directory if not found.
|
||||
|
||||
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
||||
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
||||
|
||||
set EXEC_DIR=%CD%
|
||||
set WDIR=%EXEC_DIR%
|
||||
:findBaseDir
|
||||
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
||||
cd ..
|
||||
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
||||
set WDIR=%CD%
|
||||
goto findBaseDir
|
||||
|
||||
:baseDirFound
|
||||
set MAVEN_PROJECTBASEDIR=%WDIR%
|
||||
cd "%EXEC_DIR%"
|
||||
goto endDetectBaseDir
|
||||
|
||||
:baseDirNotFound
|
||||
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
||||
cd "%EXEC_DIR%"
|
||||
|
||||
:endDetectBaseDir
|
||||
|
||||
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
|
||||
|
||||
@setlocal EnableExtensions EnableDelayedExpansion
|
||||
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
|
||||
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
|
||||
|
||||
:endReadAdditionalConfig
|
||||
|
||||
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
||||
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
|
||||
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
||||
|
||||
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||
IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
|
||||
)
|
||||
|
||||
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||
if exist %WRAPPER_JAR% (
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Found %WRAPPER_JAR%
|
||||
)
|
||||
) else (
|
||||
if not "%MVNW_REPOURL%" == "" (
|
||||
SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
||||
)
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Couldn't find %WRAPPER_JAR%, downloading it ...
|
||||
echo Downloading from: %WRAPPER_URL%
|
||||
)
|
||||
|
||||
powershell -Command "&{"^
|
||||
"$webclient = new-object System.Net.WebClient;"^
|
||||
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
|
||||
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
|
||||
"}"^
|
||||
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
|
||||
"}"
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Finished downloading %WRAPPER_JAR%
|
||||
)
|
||||
)
|
||||
@REM End of extension
|
||||
|
||||
@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
|
||||
SET WRAPPER_SHA_256_SUM=""
|
||||
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||
IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
|
||||
)
|
||||
IF NOT %WRAPPER_SHA_256_SUM%=="" (
|
||||
powershell -Command "&{"^
|
||||
"$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
|
||||
"If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
|
||||
" Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
|
||||
" Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
|
||||
" Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
|
||||
" exit 1;"^
|
||||
"}"^
|
||||
"}"
|
||||
if ERRORLEVEL 1 goto error
|
||||
)
|
||||
|
||||
@REM Provide a "standardized" way to retrieve the CLI args that will
|
||||
@REM work with both Windows and non-Windows executions.
|
||||
set MAVEN_CMD_LINE_ARGS=%*
|
||||
|
||||
%MAVEN_JAVA_EXE% ^
|
||||
%JVM_CONFIG_MAVEN_PROPS% ^
|
||||
%MAVEN_OPTS% ^
|
||||
%MAVEN_DEBUG_OPTS% ^
|
||||
-classpath %WRAPPER_JAR% ^
|
||||
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
|
||||
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
|
||||
if ERRORLEVEL 1 goto error
|
||||
goto end
|
||||
|
||||
:error
|
||||
set ERROR_CODE=1
|
||||
|
||||
:end
|
||||
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||
|
||||
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
|
||||
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
|
||||
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
|
||||
:skipRcPost
|
||||
|
||||
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
||||
if "%MAVEN_BATCH_PAUSE%"=="on" pause
|
||||
|
||||
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
|
||||
|
||||
cmd /C exit /B %ERROR_CODE%
|
||||
164
Back-end/pom.xml
@ -1,164 +0,0 @@
|
||||
<?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>com.example</groupId>
|
||||
<artifactId>starter</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
|
||||
<maven-shade-plugin.version>3.2.4</maven-shade-plugin.version>
|
||||
<maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version>
|
||||
<exec-maven-plugin.version>3.0.0</exec-maven-plugin.version>
|
||||
|
||||
<vertx.version>4.5.13</vertx.version>
|
||||
<junit-jupiter.version>5.9.1</junit-jupiter.version>
|
||||
|
||||
<main.verticle>com.example.starter.MainVerticle</main.verticle>
|
||||
<launcher.class>io.vertx.core.Launcher</launcher.class>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.vertx</groupId>
|
||||
<artifactId>vertx-stack-depchain</artifactId>
|
||||
<version>${vertx.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.vertx</groupId>
|
||||
<artifactId>vertx-core</artifactId>
|
||||
<version>4.5.13</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.agroal</groupId>
|
||||
<artifactId>agroal-api</artifactId>
|
||||
<version>1.16</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.agroal</groupId>
|
||||
<artifactId>agroal-pool</artifactId>
|
||||
<version>1.16</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.vertx</groupId>
|
||||
<artifactId>vertx-jdbc-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>42.6.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.vertx</groupId>
|
||||
<artifactId>vertx-web</artifactId>
|
||||
<version>4.4.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.vertx</groupId>
|
||||
<artifactId>vertx-junit5</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>${junit-jupiter.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>${junit-jupiter.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency> <!--Dependy pour hacher le mdp-->
|
||||
<groupId>at.favre.lib</groupId>
|
||||
<artifactId>bcrypt</artifactId>
|
||||
<version>0.9.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.vertx</groupId>
|
||||
<artifactId>vertx-auth-jwt</artifactId>
|
||||
<version>4.5.13</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${maven-compiler-plugin.version}</version>
|
||||
<configuration>
|
||||
<release>17</release>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>${maven-shade-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
<transformers>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<manifestEntries>
|
||||
<Main-Class>${launcher.class}</Main-Class>
|
||||
<Main-Verticle>${main.verticle}</Main-Verticle>
|
||||
</manifestEntries>
|
||||
</transformer>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
</transformers>
|
||||
<outputFile>${project.build.directory}/${project.artifactId}-${project.version}-fat.jar
|
||||
</outputFile>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>${maven-surefire-plugin.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>${exec-maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<mainClass>${launcher.class}</mainClass>
|
||||
<arguments>
|
||||
<argument>run</argument>
|
||||
<argument>${main.verticle}</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
||||
@ -1,169 +0,0 @@
|
||||
package com.example.starter;
|
||||
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import at.favre.lib.crypto.bcrypt.BCrypt;
|
||||
import io.vertx.ext.auth.jwt.JWTAuth;
|
||||
import io.vertx.sqlclient.Tuple;
|
||||
import io.vertx.sqlclient.Row;
|
||||
|
||||
public class AuthHandler {
|
||||
private final DatabaseService databaseService;
|
||||
private final JWTAuth jwtAuth;
|
||||
|
||||
public AuthHandler(DatabaseService databaseService, JWTAuth jwtAuth) {
|
||||
this.databaseService = databaseService;
|
||||
this.jwtAuth = jwtAuth;
|
||||
}
|
||||
|
||||
public void handleSignup(RoutingContext context) {
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
|
||||
if (body == null) {
|
||||
context.response()
|
||||
.setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Requête invalide").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
String name = body.getString("name");
|
||||
String surname = body.getString("surname");
|
||||
String email = body.getString("email");
|
||||
String gender = body.getString("gender");
|
||||
String password = body.getString("password");
|
||||
String pseudo = body.getString("pseudo");
|
||||
|
||||
if (name == null || surname == null || email == null || gender == null || password == null || pseudo == null) {
|
||||
context.response()
|
||||
.setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Tous les champs sont requis").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
String hashedPassword = BCrypt.withDefaults().hashToString(12, password.toCharArray());
|
||||
|
||||
databaseService.pool
|
||||
.preparedQuery("INSERT INTO users (name, surname, email, gender, password, pseudo) VALUES (?, ?, ?, ?, ?, ?)")
|
||||
.execute(Tuple.of(name, surname, email, gender, hashedPassword, pseudo))
|
||||
.onSuccess(result -> {
|
||||
context.response()
|
||||
.setStatusCode(201)
|
||||
.end(new JsonObject().put("message", "Utilisateur inscrit avec succès").encode());
|
||||
})
|
||||
.onFailure(err -> {
|
||||
System.err.println("Erreur d'inscription : " + err.getMessage());
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Erreur d'inscription").encode());
|
||||
});
|
||||
}
|
||||
|
||||
public void handleLogin(RoutingContext context) {
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
|
||||
if (body == null) {
|
||||
context.response()
|
||||
.setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Requête invalide").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
String email = body.getString("email");
|
||||
String password = body.getString("password");
|
||||
|
||||
if (email == null || password == null) {
|
||||
context.response()
|
||||
.setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Email et mot de passe requis").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
databaseService.pool
|
||||
.preparedQuery("SELECT id, name, surname, password, points FROM users WHERE email = ?")
|
||||
.execute(Tuple.of(email))
|
||||
.onSuccess(result -> {
|
||||
if (result.rowCount() == 0) {
|
||||
context.response()
|
||||
.setStatusCode(401)
|
||||
.end(new JsonObject().put("error", "Email ou mot de passe incorrect").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
// Récupération de la ligne de manière sécurisée
|
||||
Row row = null;
|
||||
try {
|
||||
row = result.iterator().next();
|
||||
} catch (Exception e) {
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Erreur lors de la récupération des données utilisateur").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
// Vérification que row n'est pas null avant d'y accéder
|
||||
if (row == null) {
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Données utilisateur introuvables").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
// Récupération des champs de manière sécurisée
|
||||
Integer id = row.getInteger("id");
|
||||
String storedHashedPassword = row.getString("password");
|
||||
Integer nbPointsUser = row.getInteger("points");
|
||||
String name = row.getString("name");
|
||||
String surname = row.getString("surname");
|
||||
|
||||
// Vérification des champs obligatoires
|
||||
if (id == null || storedHashedPassword == null) {
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Données utilisateur incomplètes").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
// Valeur par défaut pour les points si null
|
||||
if (nbPointsUser == null) {
|
||||
nbPointsUser = 0;
|
||||
}
|
||||
|
||||
BCrypt.Result verification = BCrypt.verifyer().verify(password.toCharArray(), storedHashedPassword);
|
||||
|
||||
if (verification.verified) {
|
||||
JsonObject claims = new JsonObject()
|
||||
.put("sub", email)
|
||||
.put("id", id);
|
||||
|
||||
// Ajout de name et surname seulement s'ils ne sont pas null
|
||||
if (name != null) claims.put("name", name);
|
||||
if (surname != null) claims.put("surname", surname);
|
||||
|
||||
// Attribution du rôle en fonction des points
|
||||
String role = "user";
|
||||
if (nbPointsUser >= 200) {
|
||||
role = "admin";
|
||||
} else if (nbPointsUser >= 100) {
|
||||
role = "complexe";
|
||||
}
|
||||
claims.put("role", role);
|
||||
|
||||
String token = jwtAuth.generateToken(claims);
|
||||
|
||||
context.response()
|
||||
.setStatusCode(200)
|
||||
.end(new JsonObject().put("token", token).encode());
|
||||
} else {
|
||||
context.response()
|
||||
.setStatusCode(401)
|
||||
.end(new JsonObject().put("error", "Email ou mot de passe incorrect").encode());
|
||||
}
|
||||
})
|
||||
.onFailure(err -> {
|
||||
System.err.println("Erreur de connexion : " + err.getMessage());
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Erreur serveur").encode());
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
package com.example.starter;
|
||||
|
||||
import io.vertx.core.Vertx;
|
||||
import io.vertx.jdbcclient.JDBCConnectOptions;
|
||||
import io.vertx.jdbcclient.JDBCPool;
|
||||
|
||||
import io.vertx.sqlclient.PoolOptions;
|
||||
|
||||
public class DatabaseService {
|
||||
JDBCPool pool;
|
||||
|
||||
public DatabaseService(Vertx vertx) {
|
||||
pool = JDBCPool.pool(vertx,
|
||||
new JDBCConnectOptions()
|
||||
.setJdbcUrl("jdbc:postgresql://" + (System.getenv("DB_HOST") != null ? System.getenv("DB_HOST") : "localhost") + ":" + (System.getenv("DB_PORT") != null ? System.getenv("DB_PORT") : "5432") + "/" + (System.getenv("DB_NAME") != null ? System.getenv("DB_NAME") : "postgres") + "?useUnicode=true&characterEncoding=UTF-8")
|
||||
.setUser(System.getenv("DB_USER") != null ? System.getenv("DB_USER") : "postgres")
|
||||
.setPassword(System.getenv("DB_PASSWORD") != null ? System.getenv("DB_PASSWORD") : "admin"),
|
||||
new PoolOptions()
|
||||
.setName("")
|
||||
.setMaxSize(16)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
package com.example.starter;
|
||||
|
||||
import io.vertx.core.Vertx;
|
||||
import io.vertx.ext.auth.jwt.JWTAuth;
|
||||
import io.vertx.ext.auth.jwt.JWTAuthOptions;
|
||||
import io.vertx.ext.auth.KeyStoreOptions;
|
||||
import com.example.starter.JwtAuthProvider;
|
||||
|
||||
|
||||
public class JwtAuthProvider {
|
||||
|
||||
public static JWTAuth createJwtAuth(Vertx vertx) {
|
||||
return JWTAuth.create(vertx, new JWTAuthOptions()
|
||||
.setKeyStore(new KeyStoreOptions()
|
||||
.setPath("keystore.jceks")
|
||||
.setPassword("secret")));
|
||||
}
|
||||
}
|
||||
@ -1,96 +0,0 @@
|
||||
package com.example.starter;
|
||||
|
||||
import io.vertx.core.AbstractVerticle;
|
||||
import io.vertx.core.Promise;
|
||||
import io.vertx.core.http.HttpMethod;
|
||||
import io.vertx.ext.web.Router;
|
||||
import io.vertx.ext.web.handler.BodyHandler;
|
||||
import io.vertx.ext.web.handler.CorsHandler;
|
||||
import io.vertx.ext.auth.jwt.JWTAuth;
|
||||
import io.vertx.ext.web.handler.JWTAuthHandler;
|
||||
|
||||
public class MainVerticle extends AbstractVerticle {
|
||||
private DatabaseService databaseService;
|
||||
private Router router;
|
||||
|
||||
@Override
|
||||
public void start(Promise<Void> startPromise) throws Exception {
|
||||
databaseService = new DatabaseService(vertx);
|
||||
|
||||
// Initialisation du fournisseur JWT
|
||||
JWTAuth jwtAuth = JwtAuthProvider.createJwtAuth(vertx);
|
||||
|
||||
// Initialisation du routeur
|
||||
router = Router.router(vertx);
|
||||
router.route().handler(BodyHandler.create());
|
||||
router.route().handler(CorsHandler.create()
|
||||
.addOrigin("*")
|
||||
.allowedMethod(HttpMethod.GET)
|
||||
.allowedMethod(HttpMethod.POST)
|
||||
.allowedHeader("Content-Type")
|
||||
.allowedHeader("Authorization"));
|
||||
|
||||
// Protéger toutes les routes commençant par "/api/"
|
||||
router.route("/api/*").handler(JWTAuthHandler.create(jwtAuth));
|
||||
|
||||
// Initialisation des handlers de requêtes
|
||||
QueryObjects queryObjects = new QueryObjects(databaseService);
|
||||
QueryWeatherData queryWeather = new QueryWeatherData(databaseService);
|
||||
SetObjects setObjects = new SetObjects(databaseService);
|
||||
SetWeatherData setWeatherData = new SetWeatherData(databaseService);
|
||||
AuthHandler authHandler = new AuthHandler(databaseService, jwtAuth);
|
||||
QueryUsers queryUsers = new QueryUsers(databaseService);
|
||||
SetUser setUser = new SetUser(databaseService);
|
||||
setObjects.setUserHandler(setUser);
|
||||
queryObjects.setUserHandler(setUser);
|
||||
setWeatherData.setUserHandler(setUser);
|
||||
QueryDeleteObject RequestDeleteObject = new QueryDeleteObject(databaseService);
|
||||
|
||||
SetRequestDeleteObject DeleteObject = new SetRequestDeleteObject(databaseService);
|
||||
|
||||
|
||||
|
||||
|
||||
// Déclaration des routes
|
||||
router.get("/objets").handler(queryObjects::getObjects);
|
||||
router.post("/objet").handler(queryObjects::getParticularObject);
|
||||
router.post("/modifObjet").handler(setObjects::setInfoObjet);
|
||||
router.get("/wind").handler(queryWeather::getWindInfos);
|
||||
router.get("/meteo").handler(queryWeather::getMeteoInfos);
|
||||
router.post("/addObject").handler(setObjects::newObject);
|
||||
router.get("/getRange").handler(queryWeather::getRangeData);
|
||||
router.post("/modifRangeData").handler(setWeatherData::setRangeData);
|
||||
router.post("/deleteObject").handler(setObjects::deleteObject);
|
||||
router.get("/users").handler(queryUsers::getUsers);
|
||||
router.post("/user").handler(queryUsers::getUser);
|
||||
router.post("/setUserPoints").handler(setUser::setUserPoints);
|
||||
router.post("/deleteUser").handler(setUser::deleteUser);
|
||||
router.post("/updateProfil").handler(setUser::updateUserProfile);
|
||||
router.post("/changePassword").handler(setUser::changeUserPassword);
|
||||
router.post("/publicUser").handler(queryUsers::getPublicUser);
|
||||
router.get("/getCategories").handler(queryObjects::getCategories);
|
||||
router.get("/getLocations").handler(queryObjects::getLocations);
|
||||
router.post("/getMeteoHome").handler(queryWeather::getMeteoInfosHomePage);
|
||||
router.post("/addCategories").handler(setObjects::newCategorie);
|
||||
router.post("/deleteCategories").handler(setObjects::deleteCategories);
|
||||
router.post("/requestDeleteObject").handler(RequestDeleteObject::deleteObject);
|
||||
router.get("/getDemandeSuppression").handler(DeleteObject::getAllDeletionRequests);
|
||||
router.post("/reject").handler(RequestDeleteObject::rejectDemande);
|
||||
// Routes d'authentification
|
||||
router.post("/signup").handler(authHandler::handleSignup);
|
||||
router.post("/login").handler(authHandler::handleLogin);
|
||||
|
||||
// Création du serveur HTTP
|
||||
vertx.createHttpServer()
|
||||
.requestHandler(router)
|
||||
.listen(8888)
|
||||
.onSuccess(server -> {
|
||||
System.out.println("HTTP server started on port " + server.actualPort());
|
||||
startPromise.complete();
|
||||
})
|
||||
.onFailure(throwable -> {
|
||||
throwable.printStackTrace();
|
||||
startPromise.fail(throwable);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,144 +0,0 @@
|
||||
package com.example.starter;
|
||||
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import io.vertx.sqlclient.Tuple;
|
||||
|
||||
public class QueryDeleteObject {
|
||||
private DatabaseService databaseService;
|
||||
|
||||
public QueryDeleteObject(DatabaseService dtbS) {
|
||||
this.databaseService = dtbS;
|
||||
}
|
||||
|
||||
public void deleteObject(RoutingContext context) {
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
|
||||
if (body == null) {
|
||||
context.response().setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Requête invalide").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
Integer objectId = body.getInteger("object_id");
|
||||
Integer userId = body.getInteger("requested_by");
|
||||
|
||||
if (objectId == null || userId == null) {
|
||||
context.response().setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Champs manquants").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
String checkQuery = "SELECT id FROM weather_objects WHERE id = ?";
|
||||
|
||||
databaseService.pool.preparedQuery(checkQuery).execute(Tuple.of(objectId), res -> {
|
||||
if (res.failed()) {
|
||||
res.cause().printStackTrace();
|
||||
context.response().setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Erreur base de données").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.result().rowCount() == 0) {
|
||||
context.response().setStatusCode(404)
|
||||
.end(new JsonObject().put("error", "Objet non trouvé").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
String checkDuplicate = "SELECT 1 FROM deletion_requests WHERE object_id = ? AND requested_by = ?";
|
||||
|
||||
databaseService.pool.preparedQuery(checkDuplicate).execute(Tuple.of(objectId, userId), duplicateCheck -> {
|
||||
if (duplicateCheck.failed()) {
|
||||
duplicateCheck.cause().printStackTrace();
|
||||
context.response().setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Erreur vérification duplication").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
if (duplicateCheck.result().rowCount() > 0) {
|
||||
context.response().setStatusCode(409)
|
||||
.end(new JsonObject().put("error", "Demande déjà existante").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
String insertQuery = "INSERT INTO deletion_requests (object_id, requested_by) VALUES (?, ?)";
|
||||
|
||||
databaseService.pool.preparedQuery(insertQuery).execute(Tuple.of(objectId, userId), insertRes -> {
|
||||
if (insertRes.succeeded()) {
|
||||
context.response().setStatusCode(200)
|
||||
.end(new JsonObject().put("message", "Demande envoyée").encode());
|
||||
} else {
|
||||
insertRes.cause().printStackTrace();
|
||||
context.response().setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Erreur insertion").encode());
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void getAllDeletionRequests(RoutingContext context) {
|
||||
String query = "SELECT * FROM deletion_requests";
|
||||
|
||||
databaseService.pool.query(query).execute(res -> {
|
||||
if (res.succeeded()) {
|
||||
var results = res.result();
|
||||
var jsonArray = new io.vertx.core.json.JsonArray();
|
||||
|
||||
results.forEach(row -> {
|
||||
JsonObject json = new JsonObject()
|
||||
.put("id", row.getInteger("id"))
|
||||
.put("object_id", row.getInteger("object_id"))
|
||||
.put("requested_by", row.getInteger("requested_by"))
|
||||
.put("requested_at", row.getLocalDateTime("requested_at").toString());
|
||||
|
||||
jsonArray.add(json);
|
||||
});
|
||||
|
||||
context.response()
|
||||
.putHeader("Content-Type", "application/json")
|
||||
.end(jsonArray.encode());
|
||||
} else {
|
||||
res.cause().printStackTrace();
|
||||
context.response().setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Erreur lors de la récupération des demandes").encode());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void rejectDemande(RoutingContext context){
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
if (body == null) {
|
||||
context.response()
|
||||
.setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Corps de la requête manquant").encode());
|
||||
return;
|
||||
}
|
||||
Integer id = body.getInteger("id");
|
||||
|
||||
|
||||
databaseService.pool
|
||||
.preparedQuery("DELETE FROM deletion_requests WHERE id=?")
|
||||
.execute(Tuple.of(id))
|
||||
.onFailure(e -> {
|
||||
System.err.println("Erreur de récupération de la BDD :" + e.getMessage());
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode());
|
||||
})
|
||||
.onSuccess(rows -> {
|
||||
if (rows.rowCount() == 0) {
|
||||
context.response()
|
||||
.setStatusCode(404)
|
||||
.end(new JsonObject().put("error", "Demande non trouvé").encode());
|
||||
return;
|
||||
}
|
||||
context.response()
|
||||
.putHeader("content-type", "application/json: charset=UTF-8")
|
||||
.end(new JsonObject().put("success", "La demande à bien été supprimé").encode());
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,162 +0,0 @@
|
||||
package com.example.starter;
|
||||
|
||||
import io.vertx.sqlclient.Row;
|
||||
import io.vertx.sqlclient.RowSet;
|
||||
import io.vertx.sqlclient.Tuple;
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
|
||||
public class QueryObjects {
|
||||
private DatabaseService databaseService;
|
||||
|
||||
public QueryObjects(DatabaseService dtbS) {
|
||||
this.databaseService = dtbS;
|
||||
}
|
||||
|
||||
private SetUser setUser;
|
||||
|
||||
public void setUserHandler(SetUser setUser) {
|
||||
this.setUser = setUser;
|
||||
}
|
||||
|
||||
public void getObjects(RoutingContext context) {
|
||||
databaseService.pool
|
||||
.query("SELECT * FROM weather_objects;")
|
||||
.execute()
|
||||
.onFailure(e -> {
|
||||
System.err.println("Erreur de récupération de la BDD :" + e.getMessage());
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode());
|
||||
})
|
||||
.onSuccess(rows -> {
|
||||
context.response()
|
||||
.putHeader("content-type", "application/json; charset=UTF-8")
|
||||
.end(getInfosObjects(rows).encode());
|
||||
});
|
||||
}
|
||||
|
||||
public void getLocations(RoutingContext context) {
|
||||
databaseService.pool
|
||||
.query("SELECT DISTINCT location FROM weather_objects;")
|
||||
.execute()
|
||||
.onFailure(e -> {
|
||||
System.err.println("Erreur de récupération de la BDD :" + e.getMessage());
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode());
|
||||
})
|
||||
.onSuccess(rows -> {
|
||||
if (!rows.iterator().hasNext()) {
|
||||
context.response()
|
||||
.setStatusCode(404)
|
||||
.end(new JsonObject().put("error", "Aucun lieu trouvé dans la base de données").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
JsonArray types = new JsonArray();
|
||||
rows.forEach(row -> types.add(row.getString("location")));
|
||||
|
||||
context.response()
|
||||
.putHeader("content-type", "application/json; charset=UTF-8")
|
||||
.end(types.encode());
|
||||
});
|
||||
}
|
||||
|
||||
public void getCategories(RoutingContext context) {
|
||||
databaseService.pool
|
||||
.query("SELECT name FROM categories;")
|
||||
.execute()
|
||||
.onFailure(e -> {
|
||||
System.err.println("Erreur de récupération de la BDD :" + e.getMessage());
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode());
|
||||
})
|
||||
.onSuccess(rows -> {
|
||||
if (!rows.iterator().hasNext()) {
|
||||
context.response()
|
||||
.setStatusCode(404)
|
||||
.end(new JsonObject().put("error", "Aucun type trouvé dans la base de données").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
JsonArray types = new JsonArray();
|
||||
rows.forEach(row -> types.add(row.getString("name")));
|
||||
|
||||
context.response()
|
||||
.putHeader("content-type", "application/json; charset=UTF-8")
|
||||
.end(types.encode());
|
||||
});
|
||||
}
|
||||
|
||||
public void getParticularObject(RoutingContext context) {
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
if (body == null) {
|
||||
context.response()
|
||||
.setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Corps de la requête manquant").encode());
|
||||
return;
|
||||
}
|
||||
String id = body.getString("id");
|
||||
|
||||
String idUser = body.getString("userId");
|
||||
if (id == null) {
|
||||
context.response()
|
||||
.setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Paramètre 'id' manquant").encode());
|
||||
return;
|
||||
}
|
||||
databaseService.pool
|
||||
.preparedQuery("SELECT * FROM weather_objects WHERE id=?")
|
||||
.execute(Tuple.of(Integer.parseInt(id)))
|
||||
.onFailure(e -> {
|
||||
System.err.println("Erreur de récupération de la BDD :" + e.getMessage());
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("Erreur", "Erreur de récupération de la BDD").encode());
|
||||
})
|
||||
.onSuccess(rows -> {
|
||||
if (rows.size() == 0) {
|
||||
context.response()
|
||||
.setStatusCode(404)
|
||||
.end(new JsonObject().put("error", "Objet non trouvé").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
Boolean shouldUpdatePoints = body.getBoolean("shouldUpdatePoints", false);
|
||||
|
||||
if (Boolean.TRUE.equals(shouldUpdatePoints) && idUser != null) {
|
||||
setUser.updateUserPoints(Integer.parseInt(idUser), 1);
|
||||
}
|
||||
context.response()
|
||||
.putHeader("content-type", "application/json: charset=UTF-8")
|
||||
.end(getInfosObjects(rows).encode());
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private JsonArray getInfosObjects(RowSet<Row> rows) {
|
||||
JsonArray objects = new JsonArray();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss");
|
||||
for (Row row : rows) {
|
||||
JsonObject object = new JsonObject()
|
||||
.put("id", row.getInteger("id"))
|
||||
.put("name", row.getString("name"))
|
||||
.put("description", row.getString("description"))
|
||||
.put("type", row.getString("type"))
|
||||
.put("location", row.getString("location"))
|
||||
.put("last_update", row.getLocalDateTime("last_update").format(formatter))
|
||||
.put("status", row.getString("status"))
|
||||
.put("batterie", row.getInteger("batterie"))
|
||||
.put("type_batterie", row.getString("type_batterie"))
|
||||
.put("proprio_id", row.getInteger("proprio_id"));
|
||||
objects.add(object);
|
||||
}
|
||||
return objects;
|
||||
}
|
||||
}
|
||||
@ -1,151 +0,0 @@
|
||||
package com.example.starter;
|
||||
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import io.vertx.sqlclient.Row;
|
||||
import io.vertx.sqlclient.Tuple;
|
||||
|
||||
public class QueryUsers {
|
||||
private DatabaseService databaseService;
|
||||
|
||||
public QueryUsers(DatabaseService dtbS) {
|
||||
this.databaseService = dtbS;
|
||||
}
|
||||
|
||||
public void getUsers(RoutingContext context) {
|
||||
databaseService.pool
|
||||
.query("SELECT * FROM users;")
|
||||
.execute()
|
||||
.onFailure(e -> {
|
||||
System.err.println("Erreur de récupération de la BDD :" + e.getMessage());
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode());
|
||||
})
|
||||
.onSuccess(rows -> {
|
||||
JsonArray users = new JsonArray();
|
||||
for (Row row : rows) {
|
||||
int points = row.getInteger("points");
|
||||
JsonObject user = new JsonObject()
|
||||
.put("id", row.getInteger("id"))
|
||||
.put("name", row.getString("name"))
|
||||
.put("surname", row.getString("surname"))
|
||||
.put("email", row.getString("email"))
|
||||
.put("gender", row.getString("gender"))
|
||||
.put("pseudo",row.getString("pseudo"))
|
||||
.put("points", points);
|
||||
if (points <= 60) {
|
||||
user.put("role", "user");
|
||||
} else if (points <= 100) {
|
||||
user.put("role", "complexe");
|
||||
} else if (points >= 200) {
|
||||
user.put("role", "admin");
|
||||
}
|
||||
users.add(user);
|
||||
}
|
||||
context.response()
|
||||
.putHeader("content-type", "application/json; charset=UTF-8")
|
||||
.end(users.encode());
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public void getUser(RoutingContext context) {
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
if (body == null) {
|
||||
context.response()
|
||||
.setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Corps de la requête manquant").encode());
|
||||
return;
|
||||
}
|
||||
Integer idUser = body.getInteger("id");
|
||||
databaseService.pool
|
||||
.preparedQuery("SELECT * FROM users WHERE id=?;")
|
||||
.execute(Tuple.of(idUser))
|
||||
.onFailure(e -> {
|
||||
System.err.println("Erreur de récupération de la BDD :" + e.getMessage());
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode());
|
||||
})
|
||||
.onSuccess(rows -> {
|
||||
if (rows.size() == 0) {
|
||||
context.response()
|
||||
.setStatusCode(404)
|
||||
.end(new JsonObject().put("error", "Utilisateur non trouvé").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
Row row = rows.iterator().next();
|
||||
int points = row.getInteger("points");
|
||||
JsonObject user = new JsonObject()
|
||||
.put("id", row.getInteger("id"))
|
||||
.put("name", row.getString("name"))
|
||||
.put("surname", row.getString("surname"))
|
||||
.put("email", row.getString("email"))
|
||||
.put("gender", row.getString("gender"))
|
||||
.put("pseudo",row.getString("pseudo"))
|
||||
.put("points", points);
|
||||
|
||||
if (points <= 60) {
|
||||
user.put("role", "user");
|
||||
} else if (points <= 100) {
|
||||
user.put("role", "complexe");
|
||||
} else if (points >= 200) {
|
||||
user.put("role", "admin");
|
||||
}
|
||||
|
||||
context.response()
|
||||
.putHeader("content-type", "application/json; charset=UTF-8")
|
||||
.end(user.encode());
|
||||
});
|
||||
}
|
||||
public void getPublicUser(RoutingContext context) {
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
if (body == null) {
|
||||
context.response()
|
||||
.setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Corps de la requête manquant").encode());
|
||||
return;
|
||||
}
|
||||
Integer idUser = body.getInteger("id");
|
||||
databaseService.pool
|
||||
.preparedQuery("SELECT * FROM users WHERE id=?;")
|
||||
.execute(Tuple.of(idUser))
|
||||
.onFailure(e -> {
|
||||
System.err.println("Erreur de récupération de la BDD :" + e.getMessage());
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode());
|
||||
})
|
||||
.onSuccess(rows -> {
|
||||
if (rows.size() == 0) {
|
||||
context.response()
|
||||
.setStatusCode(404)
|
||||
.end(new JsonObject().put("error", "Utilisateur non trouvé").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
Row row = rows.iterator().next();
|
||||
int points = row.getInteger("points");
|
||||
JsonObject user = new JsonObject()
|
||||
.put("id", row.getInteger("id"))
|
||||
.put("gender", row.getString("gender"))
|
||||
.put("pseudo",row.getString("pseudo"))
|
||||
.put("points", points);
|
||||
|
||||
if (points <= 60) {
|
||||
user.put("role", "user");
|
||||
} else if (points <= 100) {
|
||||
user.put("role", "complexe");
|
||||
} else if (points >= 200) {
|
||||
user.put("role", "admin");
|
||||
}
|
||||
|
||||
context.response()
|
||||
.putHeader("content-type", "application/json; charset=UTF-8")
|
||||
.end(user.encode());
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,166 +0,0 @@
|
||||
package com.example.starter;
|
||||
|
||||
import io.vertx.sqlclient.Row;
|
||||
import io.vertx.sqlclient.RowSet;
|
||||
import io.vertx.sqlclient.Tuple;
|
||||
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
|
||||
public class QueryWeatherData {
|
||||
private DatabaseService databaseService;
|
||||
|
||||
public QueryWeatherData(DatabaseService dtbS) {
|
||||
this.databaseService = dtbS;
|
||||
}
|
||||
|
||||
public void getWindInfos(RoutingContext context) {
|
||||
String id = context.request().getParam("id");
|
||||
if (id == null) {
|
||||
context.response()
|
||||
.setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Paramètre 'id' manquant").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
databaseService.pool
|
||||
.preparedQuery("SELECT wind_speed,wind_direction,timestamp FROM weather_data WHERE station_id=?")
|
||||
.execute(Tuple.of(Integer.parseInt(id)))
|
||||
.onFailure(e -> {
|
||||
System.err.println("Erreur de récupération de la BDD :" + e.getMessage());
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("Erreur", "Erreur de récupération de la BDD").encode());
|
||||
})
|
||||
.onSuccess(rows -> {
|
||||
if (rows.size() == 0) {
|
||||
context.response()
|
||||
.setStatusCode(404)
|
||||
.end(new JsonObject().put("error", "Objet non trouvé").encode());
|
||||
return;
|
||||
}
|
||||
context.response()
|
||||
.putHeader("content-type", "application/json: charset=UTF-8")
|
||||
.end((convertRowsToJson(rows)).encode());
|
||||
});
|
||||
}
|
||||
|
||||
public void getMeteoInfosHomePage(RoutingContext context) {
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
String location = body.getString("location");
|
||||
if (location == null) {
|
||||
context.response()
|
||||
.setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Paramètre 'location' manquant").encode());
|
||||
return;
|
||||
}
|
||||
databaseService.pool
|
||||
.query("SELECT wd.wind_speed, wd.temperature, wd.humidity, wd.pressure FROM weather_data wd " +
|
||||
"JOIN weather_objects s ON wd.station_id = s.id WHERE s.location = '" + location +
|
||||
"' ORDER BY wd.timestamp DESC LIMIT 1;")
|
||||
.execute()
|
||||
.onFailure(e -> {
|
||||
System.err.println("Erreur de récupération de la BDD : " + e.getMessage());
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode());
|
||||
})
|
||||
.onSuccess(rows -> {
|
||||
if (rows.size() == 0) {
|
||||
context.response()
|
||||
.setStatusCode(404)
|
||||
.end(new JsonObject().put("error", "Aucune donnée trouvée pour cette location").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
context.response()
|
||||
.putHeader("content-type", "application/json; charset=UTF-8")
|
||||
.end(convertRowsToJson(rows).encode());
|
||||
});
|
||||
}
|
||||
|
||||
public void getMeteoInfos(RoutingContext context) {
|
||||
String id = context.request().getParam("id");
|
||||
if (id == null) {
|
||||
context.response()
|
||||
.setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Paramètre 'id' manquant").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
databaseService.pool
|
||||
.preparedQuery("SELECT temperature,humidity,pressure,timestamp FROM weather_data WHERE station_id=?")
|
||||
.execute(Tuple.of(Integer.parseInt(id)))
|
||||
.onFailure(e -> {
|
||||
System.err.println("Erreur de récupération de la BDD :" + e.getMessage());
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("Erreur", "Erreur de récupération de la BDD").encode());
|
||||
})
|
||||
.onSuccess(rows -> {
|
||||
if (rows.size() == 0) {
|
||||
context.response()
|
||||
.setStatusCode(404)
|
||||
.end(new JsonObject().put("error", "Objet non trouvé").encode());
|
||||
return;
|
||||
}
|
||||
context.response()
|
||||
.putHeader("content-type", "application/json: charset=UTF-8")
|
||||
.end((convertRowsToJson(rows)).encode());
|
||||
});
|
||||
}
|
||||
|
||||
public void getRangeData(RoutingContext context) {
|
||||
String id = context.request().getParam("id");
|
||||
if (id == null) {
|
||||
context.response()
|
||||
.setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Paramètre 'id' manquant").encode());
|
||||
return;
|
||||
}
|
||||
databaseService.pool
|
||||
.preparedQuery(
|
||||
"SELECT temperature_min,temperature_max,pressure_min,pressure_max,humidity_min,humidity_max FROM range_data WHERE station_id=?")
|
||||
.execute(Tuple.of(Integer.parseInt(id)))
|
||||
.onFailure(e -> {
|
||||
System.err.println("Erreur de récupération de la BDD: " + e.getMessage());
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode());
|
||||
return;
|
||||
})
|
||||
.onSuccess(rows -> {
|
||||
if (rows.size() == 0) {
|
||||
context.response()
|
||||
.setStatusCode(404)
|
||||
.end(new JsonObject().put("error", "Objet non trouvé").encode());
|
||||
return;
|
||||
}
|
||||
context.response()
|
||||
.putHeader("content-type", "application/json:charset=UTF-8")
|
||||
.end((convertRowsToJson(rows)).encode());
|
||||
});
|
||||
}
|
||||
|
||||
private JsonArray convertRowsToJson(RowSet<Row> rows) {
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss");
|
||||
JsonArray jsonArray = new JsonArray();
|
||||
for (Row row : rows) {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
for (int i = 0; i < row.size(); i++) {
|
||||
String column = row.getColumnName(i);
|
||||
if (column.compareTo("timestamp") == 0) {
|
||||
jsonObject.put("timestamp", row.getLocalDateTime("timestamp").format(formatter));
|
||||
} else {
|
||||
jsonObject.put(column, row.getValue(column));
|
||||
}
|
||||
}
|
||||
jsonArray.add(jsonObject);
|
||||
}
|
||||
return jsonArray;
|
||||
}
|
||||
}
|
||||
@ -1,207 +0,0 @@
|
||||
package com.example.starter;
|
||||
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import io.vertx.sqlclient.Tuple;
|
||||
|
||||
public class SetObjects {
|
||||
private DatabaseService databaseService;
|
||||
private SetUser setUser;
|
||||
|
||||
public SetObjects(DatabaseService ddbs) {
|
||||
this.databaseService = ddbs;
|
||||
}
|
||||
|
||||
public void setUserHandler(SetUser setUser) {
|
||||
this.setUser = setUser;
|
||||
}
|
||||
|
||||
public void setInfoObjet(RoutingContext context) {
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
if (body == null) {
|
||||
context.response()
|
||||
.setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Corps de la requête manquant").encode());
|
||||
return;
|
||||
}
|
||||
Integer id = body.getInteger("id");
|
||||
Integer idUser = body.getInteger("idUser");
|
||||
String description = body.getString("description");
|
||||
String type = body.getString("type");
|
||||
String location = body.getString("location");
|
||||
String status = body.getString("status");
|
||||
|
||||
databaseService.pool
|
||||
.preparedQuery(
|
||||
"UPDATE weather_objects SET description=?,type=?,location=?,status=?,last_update=CURRENT_TIMESTAMP WHERE id=?")
|
||||
.execute(Tuple.of(description, type, location, status, id))
|
||||
.onFailure(e -> {
|
||||
System.err.println("Erreur de récupération de la BDD :" + e.getMessage());
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("Erreur", "Erreur de récupération de la BDD").encode());
|
||||
})
|
||||
.onSuccess(rows -> {
|
||||
if (rows.rowCount() == 0) {
|
||||
context.response()
|
||||
.setStatusCode(404)
|
||||
.end(new JsonObject().put("error", "Objet non trouvé").encode());
|
||||
return;
|
||||
}
|
||||
Boolean shouldUpdatePoints = body.getBoolean("shouldUpdatePoints", false);
|
||||
|
||||
if (Boolean.TRUE.equals(shouldUpdatePoints) && idUser != null) {
|
||||
setUser.updateUserPoints(idUser, 1);
|
||||
}
|
||||
context.response()
|
||||
.putHeader("content-type", "application/json: charset=UTF-8")
|
||||
.end(new JsonObject().put("success", "L'objet à bien été mis à jour").encode());
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
public void deleteCategories(RoutingContext context) {
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
if (body == null) {
|
||||
context.response()
|
||||
.setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Corps de la requête manquant").encode());
|
||||
return;
|
||||
}
|
||||
String name = body.getString("name");
|
||||
databaseService.pool
|
||||
.preparedQuery("DELETE FROM categories WHERE name=?")
|
||||
.execute(Tuple.of(name))
|
||||
.onFailure(e -> {
|
||||
System.err.println("Erreur de récupération de la BDD :" + e.getMessage());
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode());
|
||||
})
|
||||
.onSuccess(rows -> {
|
||||
if (rows.rowCount() == 0) {
|
||||
context.response()
|
||||
.setStatusCode(404)
|
||||
.end(new JsonObject().put("error", "Catégorie non trouvé").encode());
|
||||
return;
|
||||
}
|
||||
context.response()
|
||||
.putHeader("content-type", "application/json: charset=UTF-8")
|
||||
.end(new JsonObject().put("success", "La catégorie à bien été supprimé").encode());
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
public void deleteObject(RoutingContext context) {
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
if (body == null) {
|
||||
context.response()
|
||||
.setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Corps de la requête manquant").encode());
|
||||
return;
|
||||
}
|
||||
String id = body.getString("id");
|
||||
databaseService.pool
|
||||
.preparedQuery("DELETE FROM weather_objects WHERE id=?")
|
||||
.execute(Tuple.of(Integer.parseInt(id)))
|
||||
.onFailure(e -> {
|
||||
System.err.println("Erreur de récupération de la BDD :" + e.getMessage());
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode());
|
||||
})
|
||||
.onSuccess(rows -> {
|
||||
if (rows.rowCount() == 0) {
|
||||
context.response()
|
||||
.setStatusCode(404)
|
||||
.end(new JsonObject().put("error", "Objet non trouvé").encode());
|
||||
return;
|
||||
}
|
||||
context.response()
|
||||
.putHeader("content-type", "application/json: charset=UTF-8")
|
||||
.end(new JsonObject().put("success", "L'objet à bien été supprimé").encode());
|
||||
return;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void newObject(RoutingContext context) {
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
if (body == null) {
|
||||
context.response()
|
||||
.setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Corps de la requête manquant").encode());
|
||||
return;
|
||||
}
|
||||
Integer idUser = body.getInteger("proprio_id");
|
||||
String name = body.getString("nom");
|
||||
String description = body.getString("description");
|
||||
String type = body.getString("type");
|
||||
String location = body.getString("location");
|
||||
String status = body.getString("status");
|
||||
String batterieType = body.getString("batterieType");
|
||||
Integer proprio_id = body.getInteger("proprio_id");
|
||||
databaseService.pool
|
||||
.preparedQuery(
|
||||
"INSERT INTO weather_objects (name,description,type,location,status,type_batterie,proprio_id) VALUES (?,?,?,?,?,?,?)")
|
||||
.execute(Tuple.of(name, description, type, location, status, batterieType, proprio_id))
|
||||
.onFailure(e -> {
|
||||
System.err.println("Erreur de récupération de la BDD :" + e.getMessage());
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode());
|
||||
})
|
||||
.onSuccess(rows -> {
|
||||
if (rows.rowCount() == 0) {
|
||||
context.response()
|
||||
.setStatusCode(404)
|
||||
.end(new JsonObject().put("error", "Objet non trouvé").encode());
|
||||
return;
|
||||
}
|
||||
if (idUser != null) {
|
||||
setUser.updateUserPoints(idUser, 2);
|
||||
}
|
||||
;
|
||||
context.response()
|
||||
.putHeader("content-type", "application/json: charset=UTF-8")
|
||||
.end(new JsonObject().put("success", "L'objet à bien été ajouté").encode());
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
public void newCategorie(RoutingContext context) {
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
if (body == null) {
|
||||
context.response()
|
||||
.setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Corps de la requête manquant").encode());
|
||||
return;
|
||||
}
|
||||
String name = body.getString("name");
|
||||
|
||||
databaseService.pool
|
||||
.preparedQuery(
|
||||
"INSERT INTO categories (name) VALUES (?)")
|
||||
.execute(Tuple.of(name))
|
||||
.onFailure(e -> {
|
||||
System.err.println("Erreur de récupération de la BDD :" + e.getMessage());
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode());
|
||||
})
|
||||
.onSuccess(rows -> {
|
||||
if (rows.rowCount() == 0) {
|
||||
context.response()
|
||||
.setStatusCode(404)
|
||||
.end(new JsonObject().put("error", "Objet non trouvé").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
context.response()
|
||||
.putHeader("content-type", "application/json: charset=UTF-8")
|
||||
.end(new JsonObject().put("success", "La catégorie à bien été ajouté").encode());
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
package com.example.starter;
|
||||
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import io.vertx.sqlclient.Row;
|
||||
import io.vertx.sqlclient.RowSet;
|
||||
|
||||
public class SetRequestDeleteObject {
|
||||
private final DatabaseService databaseService;
|
||||
|
||||
// Le nom du constructeur doit correspondre au nom de la classe
|
||||
public SetRequestDeleteObject(DatabaseService dtbS) {
|
||||
this.databaseService = dtbS;
|
||||
}
|
||||
|
||||
public void getAllDeletionRequests(RoutingContext context) {
|
||||
String query = "SELECT * FROM deletion_requests";
|
||||
|
||||
databaseService.pool.query(query).execute(res -> {
|
||||
if (res.succeeded()) {
|
||||
RowSet<Row> results = res.result();
|
||||
JsonArray jsonArray = new JsonArray();
|
||||
|
||||
results.forEach(row -> {
|
||||
JsonObject json = new JsonObject()
|
||||
.put("id", row.getInteger("id"))
|
||||
.put("object_id", row.getInteger("object_id"))
|
||||
.put("requested_by", row.getInteger("requested_by"))
|
||||
.put("request_date", row.getLocalDateTime("request_date").toString()); // Assurez-vous que le type correspond
|
||||
jsonArray.add(json);
|
||||
});
|
||||
|
||||
context.response()
|
||||
.putHeader("Content-Type", "application/json")
|
||||
.end(jsonArray.encode());
|
||||
} else {
|
||||
res.cause().printStackTrace();
|
||||
context.response().setStatusCode(500).end(new JsonObject().put("error", "Erreur lors de la récupération des demandes").encode());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,196 +0,0 @@
|
||||
package com.example.starter;
|
||||
|
||||
import at.favre.lib.crypto.bcrypt.BCrypt;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import io.vertx.sqlclient.Tuple;
|
||||
|
||||
public class SetUser {
|
||||
private DatabaseService databaseService;
|
||||
|
||||
public SetUser(DatabaseService ddbs) {
|
||||
this.databaseService = ddbs;
|
||||
}
|
||||
|
||||
public void updateUserPoints(Integer userId, Integer points) {
|
||||
databaseService.pool
|
||||
.preparedQuery("UPDATE users SET points=points+? WHERE id=?")
|
||||
.execute(Tuple.of(points, userId))
|
||||
.onFailure(e -> {
|
||||
System.err.println("Erreur de mise à jour des points :" + e.getMessage());
|
||||
})
|
||||
.onSuccess(rows -> {
|
||||
if (rows.rowCount() > 0) {
|
||||
System.out.println("Points de l'utilisateur mis à jour avec succès");
|
||||
} else {
|
||||
System.out.println("Utilisateur non trouvé pour la mise à jour des points");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void changeUserPassword(RoutingContext context) {
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
if (body == null) {
|
||||
context.response()
|
||||
.setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Corps de la requête manquant").encode());
|
||||
return;
|
||||
}
|
||||
Integer id = body.getInteger("id");
|
||||
String oldPassword = body.getString("oldPassword");
|
||||
String newPassword = body.getString("newPassword");
|
||||
|
||||
databaseService.pool
|
||||
.preparedQuery("SELECT password FROM users WHERE id=?")
|
||||
.execute(Tuple.of(id))
|
||||
.onFailure(e -> {
|
||||
System.err.println("Erreur de récupération de la BDD :" + e.getMessage());
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode());
|
||||
})
|
||||
.onSuccess(rows -> {
|
||||
if (rows.rowCount() == 0) {
|
||||
context.response()
|
||||
.setStatusCode(404)
|
||||
.end(new JsonObject().put("error", "Utilisateur non trouvé").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
String currentPassword = rows.iterator().next().getString("password");
|
||||
BCrypt.Result verification = BCrypt.verifyer().verify(oldPassword.toCharArray(), currentPassword);
|
||||
|
||||
if (!verification.verified) {
|
||||
context.response()
|
||||
.setStatusCode(401)
|
||||
.end(new JsonObject().put("error", "Ancien mot de passe incorrect").encode());
|
||||
return;
|
||||
}
|
||||
String hashedPassword = BCrypt.withDefaults().hashToString(12, newPassword.toCharArray());
|
||||
|
||||
databaseService.pool
|
||||
.preparedQuery("UPDATE users SET password=? WHERE id=?")
|
||||
.execute(Tuple.of(hashedPassword, id))
|
||||
.onFailure(e -> {
|
||||
System.err.println("Erreur lors de la mise à jour du mot de passe :" + e.getMessage());
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject()
|
||||
.put("error", "Erreur lors de la mise à jour du mot de passe")
|
||||
.encode());
|
||||
})
|
||||
.onSuccess(updateRows -> {
|
||||
context.response()
|
||||
.putHeader("content-type", "application/json: charset=UTF-8")
|
||||
.end(new JsonObject().put("success", "Le mot de passe a bien été mis à jour")
|
||||
.encode());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public void updateUserProfile(RoutingContext context) {
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
if (body == null) {
|
||||
context.response()
|
||||
.setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Corps de la requête manquant").encode());
|
||||
return;
|
||||
}
|
||||
Integer id = body.getInteger("id");
|
||||
String name = body.getString("name");
|
||||
String surname = body.getString("surname");
|
||||
String pseudo = body.getString("pseudo");
|
||||
|
||||
databaseService.pool
|
||||
.preparedQuery("UPDATE users SET name=?, surname=?, pseudo=? WHERE id=?")
|
||||
.execute(Tuple.of(name, surname,pseudo, id))
|
||||
.onFailure(e -> {
|
||||
System.err.println("Erreur de récupération de la BDD :" + e.getMessage());
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode());
|
||||
})
|
||||
.onSuccess(rows -> {
|
||||
if (rows.rowCount() == 0) {
|
||||
context.response()
|
||||
.setStatusCode(404)
|
||||
.end(new JsonObject().put("error", "Utilisateur non trouvé").encode());
|
||||
return;
|
||||
}
|
||||
context.response()
|
||||
.putHeader("content-type", "application/json: charset=UTF-8")
|
||||
.end(new JsonObject()
|
||||
.put("success", "Les informations de l'utilisateur ont bien été mises à jour")
|
||||
.encode());
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
public void setUserPoints(RoutingContext context) {
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
if (body == null) {
|
||||
context.response()
|
||||
.setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Corps de la requête manquant").encode());
|
||||
return;
|
||||
}
|
||||
Integer id = body.getInteger("id");
|
||||
Integer points = body.getInteger("points");
|
||||
databaseService.pool
|
||||
.preparedQuery(
|
||||
"UPDATE users SET points=? WHERE id=?")
|
||||
.execute(Tuple.of(points, id))
|
||||
.onFailure(e -> {
|
||||
System.err.println("Erreur de récupération de la BDD :" + e.getMessage());
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("Erreur", "Erreur de récupération de la BDD").encode());
|
||||
})
|
||||
.onSuccess(rows -> {
|
||||
if (rows.rowCount() == 0) {
|
||||
context.response()
|
||||
.setStatusCode(404)
|
||||
.end(new JsonObject().put("error", "Utilisateur non trouvé").encode());
|
||||
return;
|
||||
}
|
||||
context.response()
|
||||
.putHeader("content-type", "application/json: charset=UTF-8")
|
||||
.end(new JsonObject().put("success", "Les points de l'utilisateur ont bien été mis à jour")
|
||||
.encode());
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
public void deleteUser(RoutingContext context) {
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
if (body == null) {
|
||||
context.response()
|
||||
.setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Corps de la requête manquant").encode());
|
||||
return;
|
||||
}
|
||||
Integer id = body.getInteger("id");
|
||||
databaseService.pool
|
||||
.preparedQuery("DELETE FROM users WHERE id=?")
|
||||
.execute(Tuple.of(id))
|
||||
.onFailure(e -> {
|
||||
System.err.println("Erreur de récupération de la BDD :" + e.getMessage());
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode());
|
||||
})
|
||||
.onSuccess(rows -> {
|
||||
if (rows.rowCount() == 0) {
|
||||
context.response()
|
||||
.setStatusCode(404)
|
||||
.end(new JsonObject().put("error", "Utilisateur non trouvé").encode());
|
||||
return;
|
||||
}
|
||||
context.response()
|
||||
.putHeader("content-type", "application/json: charset=UTF-8")
|
||||
.end(new JsonObject().put("success", "L'utilisateur à bien été supprimé").encode());
|
||||
return;
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,72 +0,0 @@
|
||||
package com.example.starter;
|
||||
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import io.vertx.sqlclient.Tuple;
|
||||
|
||||
public class SetWeatherData {
|
||||
private DatabaseService databaseService;
|
||||
private SetUser setUser;
|
||||
|
||||
|
||||
public SetWeatherData(DatabaseService ddbs) {
|
||||
this.databaseService = ddbs;
|
||||
}
|
||||
public void setUserHandler(SetUser setUser) {
|
||||
this.setUser = setUser;
|
||||
}
|
||||
public void setRangeData(RoutingContext context) {
|
||||
JsonObject body = context.body().asJsonObject();
|
||||
if (body == null) {
|
||||
context.response()
|
||||
.setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Corps de la requête manquant").encode());
|
||||
return;
|
||||
}
|
||||
String id = body.getString("id");
|
||||
Double min = body.getDouble("min");
|
||||
Double max = body.getDouble("max");
|
||||
String type = body.getString("type");
|
||||
|
||||
if (id == null || min == null || max == null || type == null) {
|
||||
context.response()
|
||||
.setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Paramètres manquants ou invalides").encode());
|
||||
return;
|
||||
}
|
||||
if (!type.matches("^[a-zA-Z_]+$")) {
|
||||
context.response()
|
||||
.setStatusCode(400)
|
||||
.end(new JsonObject().put("error", "Type invalide").encode());
|
||||
return;
|
||||
}
|
||||
String query = String.format("UPDATE range_data SET %s_min=?, %s_max=? WHERE station_id=?", type, type);
|
||||
Integer idUser = body.getInteger("idUser");
|
||||
databaseService.pool
|
||||
.preparedQuery(
|
||||
query)
|
||||
.execute(Tuple.of(min, max, Integer.parseInt(id)))
|
||||
.onFailure(e -> {
|
||||
System.err.println("Erreur de récupération de la BDD :" + e.getMessage());
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("Erreur", "Erreur de récupération de la BDD").encode());
|
||||
})
|
||||
.onSuccess(rows -> {
|
||||
if (rows.rowCount() == 0) {
|
||||
context.response()
|
||||
.setStatusCode(404)
|
||||
.end(new JsonObject().put("error", "Objet non trouvé").encode());
|
||||
return;
|
||||
}
|
||||
if (idUser != null) {
|
||||
setUser.updateUserPoints(idUser, 1);
|
||||
}
|
||||
context.response()
|
||||
.putHeader("content-type", "application/json: charset=UTF-8")
|
||||
.end(new JsonObject().put("success", "Les limites ont bien été mis à jour").encode());
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
package com.example.starter;
|
||||
|
||||
import io.vertx.core.Vertx;
|
||||
import io.vertx.junit5.VertxExtension;
|
||||
import io.vertx.junit5.VertxTestContext;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
@ExtendWith(VertxExtension.class)
|
||||
public class TestMainVerticle {
|
||||
|
||||
@BeforeEach
|
||||
void deploy_verticle(Vertx vertx, VertxTestContext testContext) {
|
||||
vertx.deployVerticle(new MainVerticle()).onComplete(testContext.succeeding(id -> testContext.completeNow()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void verticle_deployed(Vertx vertx, VertxTestContext testContext) throws Throwable {
|
||||
testContext.completeNow();
|
||||
}
|
||||
}
|
||||
24
Front-end/.gitignore
vendored
@ -1,24 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@ -1,79 +0,0 @@
|
||||
{
|
||||
"hash": "9786a6e1",
|
||||
"configHash": "fb1b2e05",
|
||||
"lockfileHash": "07bba541",
|
||||
"browserHash": "df6a5965",
|
||||
"optimized": {
|
||||
"react": {
|
||||
"src": "../../node_modules/react/index.js",
|
||||
"file": "react.js",
|
||||
"fileHash": "93e4d9cc",
|
||||
"needsInterop": true
|
||||
},
|
||||
"react-dom/client": {
|
||||
"src": "../../node_modules/react-dom/client.js",
|
||||
"file": "react-dom_client.js",
|
||||
"fileHash": "75480039",
|
||||
"needsInterop": true
|
||||
},
|
||||
"react-router-dom": {
|
||||
"src": "../../node_modules/react-router-dom/dist/index.mjs",
|
||||
"file": "react-router-dom.js",
|
||||
"fileHash": "c08021c2",
|
||||
"needsInterop": false
|
||||
},
|
||||
"jwt-decode": {
|
||||
"src": "../../node_modules/jwt-decode/build/esm/index.js",
|
||||
"file": "jwt-decode.js",
|
||||
"fileHash": "d90b24a4",
|
||||
"needsInterop": false
|
||||
},
|
||||
"lucide-react": {
|
||||
"src": "../../node_modules/lucide-react/dist/esm/lucide-react.js",
|
||||
"file": "lucide-react.js",
|
||||
"fileHash": "0b3ce2ad",
|
||||
"needsInterop": false
|
||||
},
|
||||
"axios": {
|
||||
"src": "../../node_modules/axios/index.js",
|
||||
"file": "axios.js",
|
||||
"fileHash": "ddcc2f69",
|
||||
"needsInterop": false
|
||||
},
|
||||
"recharts": {
|
||||
"src": "../../node_modules/recharts/es6/index.js",
|
||||
"file": "recharts.js",
|
||||
"fileHash": "31005dc9",
|
||||
"needsInterop": false
|
||||
},
|
||||
"react-circle-progress-bar": {
|
||||
"src": "../../node_modules/react-circle-progress-bar/dist/progress.js",
|
||||
"file": "react-circle-progress-bar.js",
|
||||
"fileHash": "39e9f2a6",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@mui/material/Slider": {
|
||||
"src": "../../node_modules/@mui/material/esm/Slider/index.js",
|
||||
"file": "@mui_material_Slider.js",
|
||||
"fileHash": "110c1e8f",
|
||||
"needsInterop": false
|
||||
}
|
||||
},
|
||||
"chunks": {
|
||||
"chunk-AG6MXCTY": {
|
||||
"file": "chunk-AG6MXCTY.js"
|
||||
},
|
||||
"chunk-JMVEG3FK": {
|
||||
"file": "chunk-JMVEG3FK.js"
|
||||
},
|
||||
"chunk-TWJRYSII": {
|
||||
"file": "chunk-TWJRYSII.js"
|
||||
},
|
||||
"chunk-PMYNNG2G": {
|
||||
"file": "chunk-PMYNNG2G.js"
|
||||
},
|
||||
"chunk-DC5AMYBS": {
|
||||
"file": "chunk-DC5AMYBS.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,680 +0,0 @@
|
||||
import {
|
||||
require_ReactPropTypesSecret,
|
||||
require_checkPropTypes,
|
||||
require_has,
|
||||
require_object_assign
|
||||
} from "./chunk-PMYNNG2G.js";
|
||||
import {
|
||||
__commonJS
|
||||
} from "./chunk-DC5AMYBS.js";
|
||||
|
||||
// node_modules/prop-types/node_modules/react-is/cjs/react-is.development.js
|
||||
var require_react_is_development = __commonJS({
|
||||
"node_modules/prop-types/node_modules/react-is/cjs/react-is.development.js"(exports) {
|
||||
"use strict";
|
||||
if (true) {
|
||||
(function() {
|
||||
"use strict";
|
||||
var hasSymbol = typeof Symbol === "function" && Symbol.for;
|
||||
var REACT_ELEMENT_TYPE = hasSymbol ? Symbol.for("react.element") : 60103;
|
||||
var REACT_PORTAL_TYPE = hasSymbol ? Symbol.for("react.portal") : 60106;
|
||||
var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol.for("react.fragment") : 60107;
|
||||
var REACT_STRICT_MODE_TYPE = hasSymbol ? Symbol.for("react.strict_mode") : 60108;
|
||||
var REACT_PROFILER_TYPE = hasSymbol ? Symbol.for("react.profiler") : 60114;
|
||||
var REACT_PROVIDER_TYPE = hasSymbol ? Symbol.for("react.provider") : 60109;
|
||||
var REACT_CONTEXT_TYPE = hasSymbol ? Symbol.for("react.context") : 60110;
|
||||
var REACT_ASYNC_MODE_TYPE = hasSymbol ? Symbol.for("react.async_mode") : 60111;
|
||||
var REACT_CONCURRENT_MODE_TYPE = hasSymbol ? Symbol.for("react.concurrent_mode") : 60111;
|
||||
var REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for("react.forward_ref") : 60112;
|
||||
var REACT_SUSPENSE_TYPE = hasSymbol ? Symbol.for("react.suspense") : 60113;
|
||||
var REACT_SUSPENSE_LIST_TYPE = hasSymbol ? Symbol.for("react.suspense_list") : 60120;
|
||||
var REACT_MEMO_TYPE = hasSymbol ? Symbol.for("react.memo") : 60115;
|
||||
var REACT_LAZY_TYPE = hasSymbol ? Symbol.for("react.lazy") : 60116;
|
||||
var REACT_BLOCK_TYPE = hasSymbol ? Symbol.for("react.block") : 60121;
|
||||
var REACT_FUNDAMENTAL_TYPE = hasSymbol ? Symbol.for("react.fundamental") : 60117;
|
||||
var REACT_RESPONDER_TYPE = hasSymbol ? Symbol.for("react.responder") : 60118;
|
||||
var REACT_SCOPE_TYPE = hasSymbol ? Symbol.for("react.scope") : 60119;
|
||||
function isValidElementType(type) {
|
||||
return typeof type === "string" || typeof type === "function" || // Note: its typeof might be other than 'symbol' or 'number' if it's a polyfill.
|
||||
type === REACT_FRAGMENT_TYPE || type === REACT_CONCURRENT_MODE_TYPE || type === REACT_PROFILER_TYPE || type === REACT_STRICT_MODE_TYPE || type === REACT_SUSPENSE_TYPE || type === REACT_SUSPENSE_LIST_TYPE || typeof type === "object" && type !== null && (type.$$typeof === REACT_LAZY_TYPE || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_PROVIDER_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE || type.$$typeof === REACT_FUNDAMENTAL_TYPE || type.$$typeof === REACT_RESPONDER_TYPE || type.$$typeof === REACT_SCOPE_TYPE || type.$$typeof === REACT_BLOCK_TYPE);
|
||||
}
|
||||
function typeOf(object) {
|
||||
if (typeof object === "object" && object !== null) {
|
||||
var $$typeof = object.$$typeof;
|
||||
switch ($$typeof) {
|
||||
case REACT_ELEMENT_TYPE:
|
||||
var type = object.type;
|
||||
switch (type) {
|
||||
case REACT_ASYNC_MODE_TYPE:
|
||||
case REACT_CONCURRENT_MODE_TYPE:
|
||||
case REACT_FRAGMENT_TYPE:
|
||||
case REACT_PROFILER_TYPE:
|
||||
case REACT_STRICT_MODE_TYPE:
|
||||
case REACT_SUSPENSE_TYPE:
|
||||
return type;
|
||||
default:
|
||||
var $$typeofType = type && type.$$typeof;
|
||||
switch ($$typeofType) {
|
||||
case REACT_CONTEXT_TYPE:
|
||||
case REACT_FORWARD_REF_TYPE:
|
||||
case REACT_LAZY_TYPE:
|
||||
case REACT_MEMO_TYPE:
|
||||
case REACT_PROVIDER_TYPE:
|
||||
return $$typeofType;
|
||||
default:
|
||||
return $$typeof;
|
||||
}
|
||||
}
|
||||
case REACT_PORTAL_TYPE:
|
||||
return $$typeof;
|
||||
}
|
||||
}
|
||||
return void 0;
|
||||
}
|
||||
var AsyncMode = REACT_ASYNC_MODE_TYPE;
|
||||
var ConcurrentMode = REACT_CONCURRENT_MODE_TYPE;
|
||||
var ContextConsumer = REACT_CONTEXT_TYPE;
|
||||
var ContextProvider = REACT_PROVIDER_TYPE;
|
||||
var Element = REACT_ELEMENT_TYPE;
|
||||
var ForwardRef = REACT_FORWARD_REF_TYPE;
|
||||
var Fragment = REACT_FRAGMENT_TYPE;
|
||||
var Lazy = REACT_LAZY_TYPE;
|
||||
var Memo = REACT_MEMO_TYPE;
|
||||
var Portal = REACT_PORTAL_TYPE;
|
||||
var Profiler = REACT_PROFILER_TYPE;
|
||||
var StrictMode = REACT_STRICT_MODE_TYPE;
|
||||
var Suspense = REACT_SUSPENSE_TYPE;
|
||||
var hasWarnedAboutDeprecatedIsAsyncMode = false;
|
||||
function isAsyncMode(object) {
|
||||
{
|
||||
if (!hasWarnedAboutDeprecatedIsAsyncMode) {
|
||||
hasWarnedAboutDeprecatedIsAsyncMode = true;
|
||||
console["warn"]("The ReactIs.isAsyncMode() alias has been deprecated, and will be removed in React 17+. Update your code to use ReactIs.isConcurrentMode() instead. It has the exact same API.");
|
||||
}
|
||||
}
|
||||
return isConcurrentMode(object) || typeOf(object) === REACT_ASYNC_MODE_TYPE;
|
||||
}
|
||||
function isConcurrentMode(object) {
|
||||
return typeOf(object) === REACT_CONCURRENT_MODE_TYPE;
|
||||
}
|
||||
function isContextConsumer(object) {
|
||||
return typeOf(object) === REACT_CONTEXT_TYPE;
|
||||
}
|
||||
function isContextProvider(object) {
|
||||
return typeOf(object) === REACT_PROVIDER_TYPE;
|
||||
}
|
||||
function isElement(object) {
|
||||
return typeof object === "object" && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;
|
||||
}
|
||||
function isForwardRef(object) {
|
||||
return typeOf(object) === REACT_FORWARD_REF_TYPE;
|
||||
}
|
||||
function isFragment(object) {
|
||||
return typeOf(object) === REACT_FRAGMENT_TYPE;
|
||||
}
|
||||
function isLazy(object) {
|
||||
return typeOf(object) === REACT_LAZY_TYPE;
|
||||
}
|
||||
function isMemo(object) {
|
||||
return typeOf(object) === REACT_MEMO_TYPE;
|
||||
}
|
||||
function isPortal(object) {
|
||||
return typeOf(object) === REACT_PORTAL_TYPE;
|
||||
}
|
||||
function isProfiler(object) {
|
||||
return typeOf(object) === REACT_PROFILER_TYPE;
|
||||
}
|
||||
function isStrictMode(object) {
|
||||
return typeOf(object) === REACT_STRICT_MODE_TYPE;
|
||||
}
|
||||
function isSuspense(object) {
|
||||
return typeOf(object) === REACT_SUSPENSE_TYPE;
|
||||
}
|
||||
exports.AsyncMode = AsyncMode;
|
||||
exports.ConcurrentMode = ConcurrentMode;
|
||||
exports.ContextConsumer = ContextConsumer;
|
||||
exports.ContextProvider = ContextProvider;
|
||||
exports.Element = Element;
|
||||
exports.ForwardRef = ForwardRef;
|
||||
exports.Fragment = Fragment;
|
||||
exports.Lazy = Lazy;
|
||||
exports.Memo = Memo;
|
||||
exports.Portal = Portal;
|
||||
exports.Profiler = Profiler;
|
||||
exports.StrictMode = StrictMode;
|
||||
exports.Suspense = Suspense;
|
||||
exports.isAsyncMode = isAsyncMode;
|
||||
exports.isConcurrentMode = isConcurrentMode;
|
||||
exports.isContextConsumer = isContextConsumer;
|
||||
exports.isContextProvider = isContextProvider;
|
||||
exports.isElement = isElement;
|
||||
exports.isForwardRef = isForwardRef;
|
||||
exports.isFragment = isFragment;
|
||||
exports.isLazy = isLazy;
|
||||
exports.isMemo = isMemo;
|
||||
exports.isPortal = isPortal;
|
||||
exports.isProfiler = isProfiler;
|
||||
exports.isStrictMode = isStrictMode;
|
||||
exports.isSuspense = isSuspense;
|
||||
exports.isValidElementType = isValidElementType;
|
||||
exports.typeOf = typeOf;
|
||||
})();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// node_modules/prop-types/node_modules/react-is/index.js
|
||||
var require_react_is = __commonJS({
|
||||
"node_modules/prop-types/node_modules/react-is/index.js"(exports, module) {
|
||||
"use strict";
|
||||
if (false) {
|
||||
module.exports = null;
|
||||
} else {
|
||||
module.exports = require_react_is_development();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// node_modules/prop-types/factoryWithTypeCheckers.js
|
||||
var require_factoryWithTypeCheckers = __commonJS({
|
||||
"node_modules/prop-types/factoryWithTypeCheckers.js"(exports, module) {
|
||||
"use strict";
|
||||
var ReactIs = require_react_is();
|
||||
var assign = require_object_assign();
|
||||
var ReactPropTypesSecret = require_ReactPropTypesSecret();
|
||||
var has = require_has();
|
||||
var checkPropTypes = require_checkPropTypes();
|
||||
var printWarning = function() {
|
||||
};
|
||||
if (true) {
|
||||
printWarning = function(text) {
|
||||
var message = "Warning: " + text;
|
||||
if (typeof console !== "undefined") {
|
||||
console.error(message);
|
||||
}
|
||||
try {
|
||||
throw new Error(message);
|
||||
} catch (x) {
|
||||
}
|
||||
};
|
||||
}
|
||||
function emptyFunctionThatReturnsNull() {
|
||||
return null;
|
||||
}
|
||||
module.exports = function(isValidElement, throwOnDirectAccess) {
|
||||
var ITERATOR_SYMBOL = typeof Symbol === "function" && Symbol.iterator;
|
||||
var FAUX_ITERATOR_SYMBOL = "@@iterator";
|
||||
function getIteratorFn(maybeIterable) {
|
||||
var iteratorFn = maybeIterable && (ITERATOR_SYMBOL && maybeIterable[ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL]);
|
||||
if (typeof iteratorFn === "function") {
|
||||
return iteratorFn;
|
||||
}
|
||||
}
|
||||
var ANONYMOUS = "<<anonymous>>";
|
||||
var ReactPropTypes = {
|
||||
array: createPrimitiveTypeChecker("array"),
|
||||
bigint: createPrimitiveTypeChecker("bigint"),
|
||||
bool: createPrimitiveTypeChecker("boolean"),
|
||||
func: createPrimitiveTypeChecker("function"),
|
||||
number: createPrimitiveTypeChecker("number"),
|
||||
object: createPrimitiveTypeChecker("object"),
|
||||
string: createPrimitiveTypeChecker("string"),
|
||||
symbol: createPrimitiveTypeChecker("symbol"),
|
||||
any: createAnyTypeChecker(),
|
||||
arrayOf: createArrayOfTypeChecker,
|
||||
element: createElementTypeChecker(),
|
||||
elementType: createElementTypeTypeChecker(),
|
||||
instanceOf: createInstanceTypeChecker,
|
||||
node: createNodeChecker(),
|
||||
objectOf: createObjectOfTypeChecker,
|
||||
oneOf: createEnumTypeChecker,
|
||||
oneOfType: createUnionTypeChecker,
|
||||
shape: createShapeTypeChecker,
|
||||
exact: createStrictShapeTypeChecker
|
||||
};
|
||||
function is(x, y) {
|
||||
if (x === y) {
|
||||
return x !== 0 || 1 / x === 1 / y;
|
||||
} else {
|
||||
return x !== x && y !== y;
|
||||
}
|
||||
}
|
||||
function PropTypeError(message, data) {
|
||||
this.message = message;
|
||||
this.data = data && typeof data === "object" ? data : {};
|
||||
this.stack = "";
|
||||
}
|
||||
PropTypeError.prototype = Error.prototype;
|
||||
function createChainableTypeChecker(validate) {
|
||||
if (true) {
|
||||
var manualPropTypeCallCache = {};
|
||||
var manualPropTypeWarningCount = 0;
|
||||
}
|
||||
function checkType(isRequired, props, propName, componentName, location, propFullName, secret) {
|
||||
componentName = componentName || ANONYMOUS;
|
||||
propFullName = propFullName || propName;
|
||||
if (secret !== ReactPropTypesSecret) {
|
||||
if (throwOnDirectAccess) {
|
||||
var err = new Error(
|
||||
"Calling PropTypes validators directly is not supported by the `prop-types` package. Use `PropTypes.checkPropTypes()` to call them. Read more at http://fb.me/use-check-prop-types"
|
||||
);
|
||||
err.name = "Invariant Violation";
|
||||
throw err;
|
||||
} else if (typeof console !== "undefined") {
|
||||
var cacheKey = componentName + ":" + propName;
|
||||
if (!manualPropTypeCallCache[cacheKey] && // Avoid spamming the console because they are often not actionable except for lib authors
|
||||
manualPropTypeWarningCount < 3) {
|
||||
printWarning(
|
||||
"You are manually calling a React.PropTypes validation function for the `" + propFullName + "` prop on `" + componentName + "`. This is deprecated and will throw in the standalone `prop-types` package. You may be seeing this warning due to a third-party PropTypes library. See https://fb.me/react-warning-dont-call-proptypes for details."
|
||||
);
|
||||
manualPropTypeCallCache[cacheKey] = true;
|
||||
manualPropTypeWarningCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (props[propName] == null) {
|
||||
if (isRequired) {
|
||||
if (props[propName] === null) {
|
||||
return new PropTypeError("The " + location + " `" + propFullName + "` is marked as required " + ("in `" + componentName + "`, but its value is `null`."));
|
||||
}
|
||||
return new PropTypeError("The " + location + " `" + propFullName + "` is marked as required in " + ("`" + componentName + "`, but its value is `undefined`."));
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
return validate(props, propName, componentName, location, propFullName);
|
||||
}
|
||||
}
|
||||
var chainedCheckType = checkType.bind(null, false);
|
||||
chainedCheckType.isRequired = checkType.bind(null, true);
|
||||
return chainedCheckType;
|
||||
}
|
||||
function createPrimitiveTypeChecker(expectedType) {
|
||||
function validate(props, propName, componentName, location, propFullName, secret) {
|
||||
var propValue = props[propName];
|
||||
var propType = getPropType(propValue);
|
||||
if (propType !== expectedType) {
|
||||
var preciseType = getPreciseType(propValue);
|
||||
return new PropTypeError(
|
||||
"Invalid " + location + " `" + propFullName + "` of type " + ("`" + preciseType + "` supplied to `" + componentName + "`, expected ") + ("`" + expectedType + "`."),
|
||||
{ expectedType }
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return createChainableTypeChecker(validate);
|
||||
}
|
||||
function createAnyTypeChecker() {
|
||||
return createChainableTypeChecker(emptyFunctionThatReturnsNull);
|
||||
}
|
||||
function createArrayOfTypeChecker(typeChecker) {
|
||||
function validate(props, propName, componentName, location, propFullName) {
|
||||
if (typeof typeChecker !== "function") {
|
||||
return new PropTypeError("Property `" + propFullName + "` of component `" + componentName + "` has invalid PropType notation inside arrayOf.");
|
||||
}
|
||||
var propValue = props[propName];
|
||||
if (!Array.isArray(propValue)) {
|
||||
var propType = getPropType(propValue);
|
||||
return new PropTypeError("Invalid " + location + " `" + propFullName + "` of type " + ("`" + propType + "` supplied to `" + componentName + "`, expected an array."));
|
||||
}
|
||||
for (var i = 0; i < propValue.length; i++) {
|
||||
var error = typeChecker(propValue, i, componentName, location, propFullName + "[" + i + "]", ReactPropTypesSecret);
|
||||
if (error instanceof Error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return createChainableTypeChecker(validate);
|
||||
}
|
||||
function createElementTypeChecker() {
|
||||
function validate(props, propName, componentName, location, propFullName) {
|
||||
var propValue = props[propName];
|
||||
if (!isValidElement(propValue)) {
|
||||
var propType = getPropType(propValue);
|
||||
return new PropTypeError("Invalid " + location + " `" + propFullName + "` of type " + ("`" + propType + "` supplied to `" + componentName + "`, expected a single ReactElement."));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return createChainableTypeChecker(validate);
|
||||
}
|
||||
function createElementTypeTypeChecker() {
|
||||
function validate(props, propName, componentName, location, propFullName) {
|
||||
var propValue = props[propName];
|
||||
if (!ReactIs.isValidElementType(propValue)) {
|
||||
var propType = getPropType(propValue);
|
||||
return new PropTypeError("Invalid " + location + " `" + propFullName + "` of type " + ("`" + propType + "` supplied to `" + componentName + "`, expected a single ReactElement type."));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return createChainableTypeChecker(validate);
|
||||
}
|
||||
function createInstanceTypeChecker(expectedClass) {
|
||||
function validate(props, propName, componentName, location, propFullName) {
|
||||
if (!(props[propName] instanceof expectedClass)) {
|
||||
var expectedClassName = expectedClass.name || ANONYMOUS;
|
||||
var actualClassName = getClassName(props[propName]);
|
||||
return new PropTypeError("Invalid " + location + " `" + propFullName + "` of type " + ("`" + actualClassName + "` supplied to `" + componentName + "`, expected ") + ("instance of `" + expectedClassName + "`."));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return createChainableTypeChecker(validate);
|
||||
}
|
||||
function createEnumTypeChecker(expectedValues) {
|
||||
if (!Array.isArray(expectedValues)) {
|
||||
if (true) {
|
||||
if (arguments.length > 1) {
|
||||
printWarning(
|
||||
"Invalid arguments supplied to oneOf, expected an array, got " + arguments.length + " arguments. A common mistake is to write oneOf(x, y, z) instead of oneOf([x, y, z])."
|
||||
);
|
||||
} else {
|
||||
printWarning("Invalid argument supplied to oneOf, expected an array.");
|
||||
}
|
||||
}
|
||||
return emptyFunctionThatReturnsNull;
|
||||
}
|
||||
function validate(props, propName, componentName, location, propFullName) {
|
||||
var propValue = props[propName];
|
||||
for (var i = 0; i < expectedValues.length; i++) {
|
||||
if (is(propValue, expectedValues[i])) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
var valuesString = JSON.stringify(expectedValues, function replacer(key, value) {
|
||||
var type = getPreciseType(value);
|
||||
if (type === "symbol") {
|
||||
return String(value);
|
||||
}
|
||||
return value;
|
||||
});
|
||||
return new PropTypeError("Invalid " + location + " `" + propFullName + "` of value `" + String(propValue) + "` " + ("supplied to `" + componentName + "`, expected one of " + valuesString + "."));
|
||||
}
|
||||
return createChainableTypeChecker(validate);
|
||||
}
|
||||
function createObjectOfTypeChecker(typeChecker) {
|
||||
function validate(props, propName, componentName, location, propFullName) {
|
||||
if (typeof typeChecker !== "function") {
|
||||
return new PropTypeError("Property `" + propFullName + "` of component `" + componentName + "` has invalid PropType notation inside objectOf.");
|
||||
}
|
||||
var propValue = props[propName];
|
||||
var propType = getPropType(propValue);
|
||||
if (propType !== "object") {
|
||||
return new PropTypeError("Invalid " + location + " `" + propFullName + "` of type " + ("`" + propType + "` supplied to `" + componentName + "`, expected an object."));
|
||||
}
|
||||
for (var key in propValue) {
|
||||
if (has(propValue, key)) {
|
||||
var error = typeChecker(propValue, key, componentName, location, propFullName + "." + key, ReactPropTypesSecret);
|
||||
if (error instanceof Error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return createChainableTypeChecker(validate);
|
||||
}
|
||||
function createUnionTypeChecker(arrayOfTypeCheckers) {
|
||||
if (!Array.isArray(arrayOfTypeCheckers)) {
|
||||
true ? printWarning("Invalid argument supplied to oneOfType, expected an instance of array.") : void 0;
|
||||
return emptyFunctionThatReturnsNull;
|
||||
}
|
||||
for (var i = 0; i < arrayOfTypeCheckers.length; i++) {
|
||||
var checker = arrayOfTypeCheckers[i];
|
||||
if (typeof checker !== "function") {
|
||||
printWarning(
|
||||
"Invalid argument supplied to oneOfType. Expected an array of check functions, but received " + getPostfixForTypeWarning(checker) + " at index " + i + "."
|
||||
);
|
||||
return emptyFunctionThatReturnsNull;
|
||||
}
|
||||
}
|
||||
function validate(props, propName, componentName, location, propFullName) {
|
||||
var expectedTypes = [];
|
||||
for (var i2 = 0; i2 < arrayOfTypeCheckers.length; i2++) {
|
||||
var checker2 = arrayOfTypeCheckers[i2];
|
||||
var checkerResult = checker2(props, propName, componentName, location, propFullName, ReactPropTypesSecret);
|
||||
if (checkerResult == null) {
|
||||
return null;
|
||||
}
|
||||
if (checkerResult.data && has(checkerResult.data, "expectedType")) {
|
||||
expectedTypes.push(checkerResult.data.expectedType);
|
||||
}
|
||||
}
|
||||
var expectedTypesMessage = expectedTypes.length > 0 ? ", expected one of type [" + expectedTypes.join(", ") + "]" : "";
|
||||
return new PropTypeError("Invalid " + location + " `" + propFullName + "` supplied to " + ("`" + componentName + "`" + expectedTypesMessage + "."));
|
||||
}
|
||||
return createChainableTypeChecker(validate);
|
||||
}
|
||||
function createNodeChecker() {
|
||||
function validate(props, propName, componentName, location, propFullName) {
|
||||
if (!isNode(props[propName])) {
|
||||
return new PropTypeError("Invalid " + location + " `" + propFullName + "` supplied to " + ("`" + componentName + "`, expected a ReactNode."));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return createChainableTypeChecker(validate);
|
||||
}
|
||||
function invalidValidatorError(componentName, location, propFullName, key, type) {
|
||||
return new PropTypeError(
|
||||
(componentName || "React class") + ": " + location + " type `" + propFullName + "." + key + "` is invalid; it must be a function, usually from the `prop-types` package, but received `" + type + "`."
|
||||
);
|
||||
}
|
||||
function createShapeTypeChecker(shapeTypes) {
|
||||
function validate(props, propName, componentName, location, propFullName) {
|
||||
var propValue = props[propName];
|
||||
var propType = getPropType(propValue);
|
||||
if (propType !== "object") {
|
||||
return new PropTypeError("Invalid " + location + " `" + propFullName + "` of type `" + propType + "` " + ("supplied to `" + componentName + "`, expected `object`."));
|
||||
}
|
||||
for (var key in shapeTypes) {
|
||||
var checker = shapeTypes[key];
|
||||
if (typeof checker !== "function") {
|
||||
return invalidValidatorError(componentName, location, propFullName, key, getPreciseType(checker));
|
||||
}
|
||||
var error = checker(propValue, key, componentName, location, propFullName + "." + key, ReactPropTypesSecret);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return createChainableTypeChecker(validate);
|
||||
}
|
||||
function createStrictShapeTypeChecker(shapeTypes) {
|
||||
function validate(props, propName, componentName, location, propFullName) {
|
||||
var propValue = props[propName];
|
||||
var propType = getPropType(propValue);
|
||||
if (propType !== "object") {
|
||||
return new PropTypeError("Invalid " + location + " `" + propFullName + "` of type `" + propType + "` " + ("supplied to `" + componentName + "`, expected `object`."));
|
||||
}
|
||||
var allKeys = assign({}, props[propName], shapeTypes);
|
||||
for (var key in allKeys) {
|
||||
var checker = shapeTypes[key];
|
||||
if (has(shapeTypes, key) && typeof checker !== "function") {
|
||||
return invalidValidatorError(componentName, location, propFullName, key, getPreciseType(checker));
|
||||
}
|
||||
if (!checker) {
|
||||
return new PropTypeError(
|
||||
"Invalid " + location + " `" + propFullName + "` key `" + key + "` supplied to `" + componentName + "`.\nBad object: " + JSON.stringify(props[propName], null, " ") + "\nValid keys: " + JSON.stringify(Object.keys(shapeTypes), null, " ")
|
||||
);
|
||||
}
|
||||
var error = checker(propValue, key, componentName, location, propFullName + "." + key, ReactPropTypesSecret);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return createChainableTypeChecker(validate);
|
||||
}
|
||||
function isNode(propValue) {
|
||||
switch (typeof propValue) {
|
||||
case "number":
|
||||
case "string":
|
||||
case "undefined":
|
||||
return true;
|
||||
case "boolean":
|
||||
return !propValue;
|
||||
case "object":
|
||||
if (Array.isArray(propValue)) {
|
||||
return propValue.every(isNode);
|
||||
}
|
||||
if (propValue === null || isValidElement(propValue)) {
|
||||
return true;
|
||||
}
|
||||
var iteratorFn = getIteratorFn(propValue);
|
||||
if (iteratorFn) {
|
||||
var iterator = iteratorFn.call(propValue);
|
||||
var step;
|
||||
if (iteratorFn !== propValue.entries) {
|
||||
while (!(step = iterator.next()).done) {
|
||||
if (!isNode(step.value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (!(step = iterator.next()).done) {
|
||||
var entry = step.value;
|
||||
if (entry) {
|
||||
if (!isNode(entry[1])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function isSymbol(propType, propValue) {
|
||||
if (propType === "symbol") {
|
||||
return true;
|
||||
}
|
||||
if (!propValue) {
|
||||
return false;
|
||||
}
|
||||
if (propValue["@@toStringTag"] === "Symbol") {
|
||||
return true;
|
||||
}
|
||||
if (typeof Symbol === "function" && propValue instanceof Symbol) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function getPropType(propValue) {
|
||||
var propType = typeof propValue;
|
||||
if (Array.isArray(propValue)) {
|
||||
return "array";
|
||||
}
|
||||
if (propValue instanceof RegExp) {
|
||||
return "object";
|
||||
}
|
||||
if (isSymbol(propType, propValue)) {
|
||||
return "symbol";
|
||||
}
|
||||
return propType;
|
||||
}
|
||||
function getPreciseType(propValue) {
|
||||
if (typeof propValue === "undefined" || propValue === null) {
|
||||
return "" + propValue;
|
||||
}
|
||||
var propType = getPropType(propValue);
|
||||
if (propType === "object") {
|
||||
if (propValue instanceof Date) {
|
||||
return "date";
|
||||
} else if (propValue instanceof RegExp) {
|
||||
return "regexp";
|
||||
}
|
||||
}
|
||||
return propType;
|
||||
}
|
||||
function getPostfixForTypeWarning(value) {
|
||||
var type = getPreciseType(value);
|
||||
switch (type) {
|
||||
case "array":
|
||||
case "object":
|
||||
return "an " + type;
|
||||
case "boolean":
|
||||
case "date":
|
||||
case "regexp":
|
||||
return "a " + type;
|
||||
default:
|
||||
return type;
|
||||
}
|
||||
}
|
||||
function getClassName(propValue) {
|
||||
if (!propValue.constructor || !propValue.constructor.name) {
|
||||
return ANONYMOUS;
|
||||
}
|
||||
return propValue.constructor.name;
|
||||
}
|
||||
ReactPropTypes.checkPropTypes = checkPropTypes;
|
||||
ReactPropTypes.resetWarningCache = checkPropTypes.resetWarningCache;
|
||||
ReactPropTypes.PropTypes = ReactPropTypes;
|
||||
return ReactPropTypes;
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// node_modules/prop-types/index.js
|
||||
var require_prop_types = __commonJS({
|
||||
"node_modules/prop-types/index.js"(exports, module) {
|
||||
if (true) {
|
||||
ReactIs = require_react_is();
|
||||
throwOnDirectAccess = true;
|
||||
module.exports = require_factoryWithTypeCheckers()(ReactIs.isElement, throwOnDirectAccess);
|
||||
} else {
|
||||
module.exports = null();
|
||||
}
|
||||
var ReactIs;
|
||||
var throwOnDirectAccess;
|
||||
}
|
||||
});
|
||||
|
||||
// node_modules/clsx/dist/clsx.mjs
|
||||
function r(e) {
|
||||
var t, f, n = "";
|
||||
if ("string" == typeof e || "number" == typeof e) n += e;
|
||||
else if ("object" == typeof e) if (Array.isArray(e)) {
|
||||
var o = e.length;
|
||||
for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
|
||||
} else for (f in e) e[f] && (n && (n += " "), n += f);
|
||||
return n;
|
||||
}
|
||||
function clsx() {
|
||||
for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
|
||||
return n;
|
||||
}
|
||||
var clsx_default = clsx;
|
||||
|
||||
// node_modules/@babel/runtime/helpers/esm/extends.js
|
||||
function _extends() {
|
||||
return _extends = Object.assign ? Object.assign.bind() : function(n) {
|
||||
for (var e = 1; e < arguments.length; e++) {
|
||||
var t = arguments[e];
|
||||
for (var r2 in t) ({}).hasOwnProperty.call(t, r2) && (n[r2] = t[r2]);
|
||||
}
|
||||
return n;
|
||||
}, _extends.apply(null, arguments);
|
||||
}
|
||||
|
||||
export {
|
||||
clsx_default,
|
||||
require_prop_types,
|
||||
_extends
|
||||
};
|
||||
/*! Bundled license information:
|
||||
|
||||
react-is/cjs/react-is.development.js:
|
||||
(** @license React v16.13.1
|
||||
* react-is.development.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*)
|
||||
*/
|
||||
//# sourceMappingURL=chunk-AG6MXCTY.js.map
|
||||
@ -1,39 +0,0 @@
|
||||
var __create = Object.create;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
||||
var __commonJS = (cb, mod) => function __require() {
|
||||
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
||||
};
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
||||
// If the importer is in node compatibility mode or this is not an ESM
|
||||
// file that has been converted to a CommonJS file using a Babel-
|
||||
// compatible transform (i.e. "__esModule" has not been set), then set
|
||||
// "default" to the CommonJS "module.exports" for node compatibility.
|
||||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
||||
mod
|
||||
));
|
||||
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
||||
|
||||
export {
|
||||
__commonJS,
|
||||
__export,
|
||||
__toESM,
|
||||
__publicField
|
||||
};
|
||||
//# sourceMappingURL=chunk-DC5AMYBS.js.map
|
||||
@ -1,7 +0,0 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": [],
|
||||
"sourcesContent": [],
|
||||
"mappings": "",
|
||||
"names": []
|
||||
}
|
||||
@ -1,172 +0,0 @@
|
||||
import {
|
||||
__commonJS
|
||||
} from "./chunk-DC5AMYBS.js";
|
||||
|
||||
// node_modules/object-assign/index.js
|
||||
var require_object_assign = __commonJS({
|
||||
"node_modules/object-assign/index.js"(exports, module) {
|
||||
"use strict";
|
||||
var getOwnPropertySymbols = Object.getOwnPropertySymbols;
|
||||
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
var propIsEnumerable = Object.prototype.propertyIsEnumerable;
|
||||
function toObject(val) {
|
||||
if (val === null || val === void 0) {
|
||||
throw new TypeError("Object.assign cannot be called with null or undefined");
|
||||
}
|
||||
return Object(val);
|
||||
}
|
||||
function shouldUseNative() {
|
||||
try {
|
||||
if (!Object.assign) {
|
||||
return false;
|
||||
}
|
||||
var test1 = new String("abc");
|
||||
test1[5] = "de";
|
||||
if (Object.getOwnPropertyNames(test1)[0] === "5") {
|
||||
return false;
|
||||
}
|
||||
var test2 = {};
|
||||
for (var i = 0; i < 10; i++) {
|
||||
test2["_" + String.fromCharCode(i)] = i;
|
||||
}
|
||||
var order2 = Object.getOwnPropertyNames(test2).map(function(n) {
|
||||
return test2[n];
|
||||
});
|
||||
if (order2.join("") !== "0123456789") {
|
||||
return false;
|
||||
}
|
||||
var test3 = {};
|
||||
"abcdefghijklmnopqrst".split("").forEach(function(letter) {
|
||||
test3[letter] = letter;
|
||||
});
|
||||
if (Object.keys(Object.assign({}, test3)).join("") !== "abcdefghijklmnopqrst") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
module.exports = shouldUseNative() ? Object.assign : function(target, source) {
|
||||
var from;
|
||||
var to = toObject(target);
|
||||
var symbols;
|
||||
for (var s = 1; s < arguments.length; s++) {
|
||||
from = Object(arguments[s]);
|
||||
for (var key in from) {
|
||||
if (hasOwnProperty.call(from, key)) {
|
||||
to[key] = from[key];
|
||||
}
|
||||
}
|
||||
if (getOwnPropertySymbols) {
|
||||
symbols = getOwnPropertySymbols(from);
|
||||
for (var i = 0; i < symbols.length; i++) {
|
||||
if (propIsEnumerable.call(from, symbols[i])) {
|
||||
to[symbols[i]] = from[symbols[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return to;
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// node_modules/prop-types/lib/ReactPropTypesSecret.js
|
||||
var require_ReactPropTypesSecret = __commonJS({
|
||||
"node_modules/prop-types/lib/ReactPropTypesSecret.js"(exports, module) {
|
||||
"use strict";
|
||||
var ReactPropTypesSecret = "SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED";
|
||||
module.exports = ReactPropTypesSecret;
|
||||
}
|
||||
});
|
||||
|
||||
// node_modules/prop-types/lib/has.js
|
||||
var require_has = __commonJS({
|
||||
"node_modules/prop-types/lib/has.js"(exports, module) {
|
||||
module.exports = Function.call.bind(Object.prototype.hasOwnProperty);
|
||||
}
|
||||
});
|
||||
|
||||
// node_modules/prop-types/checkPropTypes.js
|
||||
var require_checkPropTypes = __commonJS({
|
||||
"node_modules/prop-types/checkPropTypes.js"(exports, module) {
|
||||
"use strict";
|
||||
var printWarning = function() {
|
||||
};
|
||||
if (true) {
|
||||
ReactPropTypesSecret = require_ReactPropTypesSecret();
|
||||
loggedTypeFailures = {};
|
||||
has = require_has();
|
||||
printWarning = function(text) {
|
||||
var message = "Warning: " + text;
|
||||
if (typeof console !== "undefined") {
|
||||
console.error(message);
|
||||
}
|
||||
try {
|
||||
throw new Error(message);
|
||||
} catch (x) {
|
||||
}
|
||||
};
|
||||
}
|
||||
var ReactPropTypesSecret;
|
||||
var loggedTypeFailures;
|
||||
var has;
|
||||
function checkPropTypes(typeSpecs, values, location, componentName, getStack) {
|
||||
if (true) {
|
||||
for (var typeSpecName in typeSpecs) {
|
||||
if (has(typeSpecs, typeSpecName)) {
|
||||
var error;
|
||||
try {
|
||||
if (typeof typeSpecs[typeSpecName] !== "function") {
|
||||
var err = Error(
|
||||
(componentName || "React class") + ": " + location + " type `" + typeSpecName + "` is invalid; it must be a function, usually from the `prop-types` package, but received `" + typeof typeSpecs[typeSpecName] + "`.This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`."
|
||||
);
|
||||
err.name = "Invariant Violation";
|
||||
throw err;
|
||||
}
|
||||
error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret);
|
||||
} catch (ex) {
|
||||
error = ex;
|
||||
}
|
||||
if (error && !(error instanceof Error)) {
|
||||
printWarning(
|
||||
(componentName || "React class") + ": type specification of " + location + " `" + typeSpecName + "` is invalid; the type checker function must return `null` or an `Error` but returned a " + typeof error + ". You may have forgotten to pass an argument to the type checker creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and shape all require an argument)."
|
||||
);
|
||||
}
|
||||
if (error instanceof Error && !(error.message in loggedTypeFailures)) {
|
||||
loggedTypeFailures[error.message] = true;
|
||||
var stack = getStack ? getStack() : "";
|
||||
printWarning(
|
||||
"Failed " + location + " type: " + error.message + (stack != null ? stack : "")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
checkPropTypes.resetWarningCache = function() {
|
||||
if (true) {
|
||||
loggedTypeFailures = {};
|
||||
}
|
||||
};
|
||||
module.exports = checkPropTypes;
|
||||
}
|
||||
});
|
||||
|
||||
export {
|
||||
require_object_assign,
|
||||
require_ReactPropTypesSecret,
|
||||
require_has,
|
||||
require_checkPropTypes
|
||||
};
|
||||
/*! Bundled license information:
|
||||
|
||||
object-assign/index.js:
|
||||
(*
|
||||
object-assign
|
||||
(c) Sindre Sorhus
|
||||
@license MIT
|
||||
*)
|
||||
*/
|
||||
//# sourceMappingURL=chunk-PMYNNG2G.js.map
|
||||
@ -1,62 +0,0 @@
|
||||
import "./chunk-DC5AMYBS.js";
|
||||
|
||||
// node_modules/jwt-decode/build/esm/index.js
|
||||
var InvalidTokenError = class extends Error {
|
||||
};
|
||||
InvalidTokenError.prototype.name = "InvalidTokenError";
|
||||
function b64DecodeUnicode(str) {
|
||||
return decodeURIComponent(atob(str).replace(/(.)/g, (m, p) => {
|
||||
let code = p.charCodeAt(0).toString(16).toUpperCase();
|
||||
if (code.length < 2) {
|
||||
code = "0" + code;
|
||||
}
|
||||
return "%" + code;
|
||||
}));
|
||||
}
|
||||
function base64UrlDecode(str) {
|
||||
let output = str.replace(/-/g, "+").replace(/_/g, "/");
|
||||
switch (output.length % 4) {
|
||||
case 0:
|
||||
break;
|
||||
case 2:
|
||||
output += "==";
|
||||
break;
|
||||
case 3:
|
||||
output += "=";
|
||||
break;
|
||||
default:
|
||||
throw new Error("base64 string is not of the correct length");
|
||||
}
|
||||
try {
|
||||
return b64DecodeUnicode(output);
|
||||
} catch (err) {
|
||||
return atob(output);
|
||||
}
|
||||
}
|
||||
function jwtDecode(token, options) {
|
||||
if (typeof token !== "string") {
|
||||
throw new InvalidTokenError("Invalid token specified: must be a string");
|
||||
}
|
||||
options || (options = {});
|
||||
const pos = options.header === true ? 0 : 1;
|
||||
const part = token.split(".")[pos];
|
||||
if (typeof part !== "string") {
|
||||
throw new InvalidTokenError(`Invalid token specified: missing part #${pos + 1}`);
|
||||
}
|
||||
let decoded;
|
||||
try {
|
||||
decoded = base64UrlDecode(part);
|
||||
} catch (e) {
|
||||
throw new InvalidTokenError(`Invalid token specified: invalid base64 for part #${pos + 1} (${e.message})`);
|
||||
}
|
||||
try {
|
||||
return JSON.parse(decoded);
|
||||
} catch (e) {
|
||||
throw new InvalidTokenError(`Invalid token specified: invalid json for part #${pos + 1} (${e.message})`);
|
||||
}
|
||||
}
|
||||
export {
|
||||
InvalidTokenError,
|
||||
jwtDecode
|
||||
};
|
||||
//# sourceMappingURL=jwt-decode.js.map
|
||||
@ -1,7 +0,0 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": ["../../node_modules/jwt-decode/build/esm/index.js"],
|
||||
"sourcesContent": ["export class InvalidTokenError extends Error {\n}\nInvalidTokenError.prototype.name = \"InvalidTokenError\";\nfunction b64DecodeUnicode(str) {\n return decodeURIComponent(atob(str).replace(/(.)/g, (m, p) => {\n let code = p.charCodeAt(0).toString(16).toUpperCase();\n if (code.length < 2) {\n code = \"0\" + code;\n }\n return \"%\" + code;\n }));\n}\nfunction base64UrlDecode(str) {\n let output = str.replace(/-/g, \"+\").replace(/_/g, \"/\");\n switch (output.length % 4) {\n case 0:\n break;\n case 2:\n output += \"==\";\n break;\n case 3:\n output += \"=\";\n break;\n default:\n throw new Error(\"base64 string is not of the correct length\");\n }\n try {\n return b64DecodeUnicode(output);\n }\n catch (err) {\n return atob(output);\n }\n}\nexport function jwtDecode(token, options) {\n if (typeof token !== \"string\") {\n throw new InvalidTokenError(\"Invalid token specified: must be a string\");\n }\n options || (options = {});\n const pos = options.header === true ? 0 : 1;\n const part = token.split(\".\")[pos];\n if (typeof part !== \"string\") {\n throw new InvalidTokenError(`Invalid token specified: missing part #${pos + 1}`);\n }\n let decoded;\n try {\n decoded = base64UrlDecode(part);\n }\n catch (e) {\n throw new InvalidTokenError(`Invalid token specified: invalid base64 for part #${pos + 1} (${e.message})`);\n }\n try {\n return JSON.parse(decoded);\n }\n catch (e) {\n throw new InvalidTokenError(`Invalid token specified: invalid json for part #${pos + 1} (${e.message})`);\n }\n}\n"],
|
||||
"mappings": ";;;AAAO,IAAM,oBAAN,cAAgC,MAAM;AAC7C;AACA,kBAAkB,UAAU,OAAO;AACnC,SAAS,iBAAiB,KAAK;AAC3B,SAAO,mBAAmB,KAAK,GAAG,EAAE,QAAQ,QAAQ,CAAC,GAAG,MAAM;AAC1D,QAAI,OAAO,EAAE,WAAW,CAAC,EAAE,SAAS,EAAE,EAAE,YAAY;AACpD,QAAI,KAAK,SAAS,GAAG;AACjB,aAAO,MAAM;AAAA,IACjB;AACA,WAAO,MAAM;AAAA,EACjB,CAAC,CAAC;AACN;AACA,SAAS,gBAAgB,KAAK;AAC1B,MAAI,SAAS,IAAI,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG;AACrD,UAAQ,OAAO,SAAS,GAAG;AAAA,IACvB,KAAK;AACD;AAAA,IACJ,KAAK;AACD,gBAAU;AACV;AAAA,IACJ,KAAK;AACD,gBAAU;AACV;AAAA,IACJ;AACI,YAAM,IAAI,MAAM,4CAA4C;AAAA,EACpE;AACA,MAAI;AACA,WAAO,iBAAiB,MAAM;AAAA,EAClC,SACO,KAAK;AACR,WAAO,KAAK,MAAM;AAAA,EACtB;AACJ;AACO,SAAS,UAAU,OAAO,SAAS;AACtC,MAAI,OAAO,UAAU,UAAU;AAC3B,UAAM,IAAI,kBAAkB,2CAA2C;AAAA,EAC3E;AACA,cAAY,UAAU,CAAC;AACvB,QAAM,MAAM,QAAQ,WAAW,OAAO,IAAI;AAC1C,QAAM,OAAO,MAAM,MAAM,GAAG,EAAE,GAAG;AACjC,MAAI,OAAO,SAAS,UAAU;AAC1B,UAAM,IAAI,kBAAkB,0CAA0C,MAAM,CAAC,EAAE;AAAA,EACnF;AACA,MAAI;AACJ,MAAI;AACA,cAAU,gBAAgB,IAAI;AAAA,EAClC,SACO,GAAG;AACN,UAAM,IAAI,kBAAkB,qDAAqD,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG;AAAA,EAC7G;AACA,MAAI;AACA,WAAO,KAAK,MAAM,OAAO;AAAA,EAC7B,SACO,GAAG;AACN,UAAM,IAAI,kBAAkB,mDAAmD,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG;AAAA,EAC3G;AACJ;",
|
||||
"names": []
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
||||
1471
Front-end/.vite/deps/react-circle-progress-bar.js
vendored
39
Front-end/.vite/deps/react-dom_client.js
vendored
@ -1,39 +0,0 @@
|
||||
import {
|
||||
require_react_dom
|
||||
} from "./chunk-JMVEG3FK.js";
|
||||
import "./chunk-TWJRYSII.js";
|
||||
import {
|
||||
__commonJS
|
||||
} from "./chunk-DC5AMYBS.js";
|
||||
|
||||
// node_modules/react-dom/client.js
|
||||
var require_client = __commonJS({
|
||||
"node_modules/react-dom/client.js"(exports) {
|
||||
var m = require_react_dom();
|
||||
if (false) {
|
||||
exports.createRoot = m.createRoot;
|
||||
exports.hydrateRoot = m.hydrateRoot;
|
||||
} else {
|
||||
i = m.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
|
||||
exports.createRoot = function(c, o) {
|
||||
i.usingClientEntryPoint = true;
|
||||
try {
|
||||
return m.createRoot(c, o);
|
||||
} finally {
|
||||
i.usingClientEntryPoint = false;
|
||||
}
|
||||
};
|
||||
exports.hydrateRoot = function(c, h, o) {
|
||||
i.usingClientEntryPoint = true;
|
||||
try {
|
||||
return m.hydrateRoot(c, h, o);
|
||||
} finally {
|
||||
i.usingClientEntryPoint = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
var i;
|
||||
}
|
||||
});
|
||||
export default require_client();
|
||||
//# sourceMappingURL=react-dom_client.js.map
|
||||
@ -1,7 +0,0 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": ["../../node_modules/react-dom/client.js"],
|
||||
"sourcesContent": ["'use strict';\n\nvar m = require('react-dom');\nif (process.env.NODE_ENV === 'production') {\n exports.createRoot = m.createRoot;\n exports.hydrateRoot = m.hydrateRoot;\n} else {\n var i = m.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;\n exports.createRoot = function(c, o) {\n i.usingClientEntryPoint = true;\n try {\n return m.createRoot(c, o);\n } finally {\n i.usingClientEntryPoint = false;\n }\n };\n exports.hydrateRoot = function(c, h, o) {\n i.usingClientEntryPoint = true;\n try {\n return m.hydrateRoot(c, h, o);\n } finally {\n i.usingClientEntryPoint = false;\n }\n };\n}\n"],
|
||||
"mappings": ";;;;;;;;;AAAA;AAAA;AAEA,QAAI,IAAI;AACR,QAAI,OAAuC;AACzC,cAAQ,aAAa,EAAE;AACvB,cAAQ,cAAc,EAAE;AAAA,IAC1B,OAAO;AACD,UAAI,EAAE;AACV,cAAQ,aAAa,SAAS,GAAG,GAAG;AAClC,UAAE,wBAAwB;AAC1B,YAAI;AACF,iBAAO,EAAE,WAAW,GAAG,CAAC;AAAA,QAC1B,UAAE;AACA,YAAE,wBAAwB;AAAA,QAC5B;AAAA,MACF;AACA,cAAQ,cAAc,SAAS,GAAG,GAAG,GAAG;AACtC,UAAE,wBAAwB;AAC1B,YAAI;AACF,iBAAO,EAAE,YAAY,GAAG,GAAG,CAAC;AAAA,QAC9B,UAAE;AACA,YAAE,wBAAwB;AAAA,QAC5B;AAAA,MACF;AAAA,IACF;AAjBM;AAAA;AAAA;",
|
||||
"names": []
|
||||
}
|
||||
11494
Front-end/.vite/deps/react-router-dom.js
vendored
6
Front-end/.vite/deps/react.js
vendored
@ -1,6 +0,0 @@
|
||||
import {
|
||||
require_react
|
||||
} from "./chunk-TWJRYSII.js";
|
||||
import "./chunk-DC5AMYBS.js";
|
||||
export default require_react();
|
||||
//# sourceMappingURL=react.js.map
|
||||
@ -1,7 +0,0 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": [],
|
||||
"sourcesContent": [],
|
||||
"mappings": "",
|
||||
"names": []
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
# Stage 1: Build the React application
|
||||
FROM node:18-alpine AS build
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json package-lock.json* ./
|
||||
RUN npm install
|
||||
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
# Stage 2: Serve the application using Nginx
|
||||
FROM nginx:alpine
|
||||
|
||||
# Remove default Nginx static assets
|
||||
RUN rm -rf /usr/share/nginx/html/*
|
||||
|
||||
# Copy built app from the first stage
|
||||
COPY --from=build /app/dist /usr/share/nginx/html
|
||||
|
||||
# Replace the default Nginx configuration
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
@ -1,27 +0,0 @@
|
||||
import js from '@eslint/js';
|
||||
import globals from 'globals';
|
||||
import reactHooks from 'eslint-plugin-react-hooks';
|
||||
import reactRefresh from 'eslint-plugin-react-refresh';
|
||||
|
||||
export default [
|
||||
{ ignores: ['dist'] },
|
||||
js.configs.recommended,
|
||||
{
|
||||
files: ['**/*.{js,jsx}'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
},
|
||||
plugins: {
|
||||
'react-hooks': reactHooks,
|
||||
'react-refresh': reactRefresh,
|
||||
},
|
||||
rules: {
|
||||
...reactHooks.configs.recommended.rules,
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
},
|
||||
}
|
||||
];
|
||||
@ -1,14 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="icon" href=".//img/cloud-sun-rain.svg"/>
|
||||
<title>Projet Dev Web</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,12 +0,0 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
root /usr/share/nginx/html;
|
||||
|
||||
index index.html;
|
||||
|
||||
# Fallback to index.html for Single Page Applications handling client-side routing
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
6792
Front-end/package-lock.json
generated
@ -1,55 +0,0 @@
|
||||
{
|
||||
"name": "vite-react-javascript-starter",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.0",
|
||||
"@mui/material": "^7.0.1",
|
||||
"axios": "^1.8.4",
|
||||
"i18next": "^26.0.1",
|
||||
"i18next-browser-languagedetector": "^8.2.1",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"lucide-react": "^0.427.0",
|
||||
"react": "^18.3.1",
|
||||
"react-charts": "^3.0.0-beta.57",
|
||||
"react-circle-progress-bar": "^0.1.4",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-i18next": "^17.0.1",
|
||||
"react-router-dom": "^7.4.0",
|
||||
"recharts": "^2.15.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.9.1",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"autoprefixer": "^10.4.18",
|
||||
"eslint": "^9.23.0",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.11",
|
||||
"globals": "^15.9.0",
|
||||
"postcss": "^8.4.35",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"vite": "^5.4.2"
|
||||
},
|
||||
"description": "Bienvenue dans le projet **DevWeb** ! Ce projet utilise **Vite** et **React** pour créer une application web moderne et performante.",
|
||||
"main": "eslint.config.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/Charles40130/Projet-Dev-Web-Ing1.git"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/Charles40130/Projet-Dev-Web-Ing1/issues"
|
||||
},
|
||||
"homepage": "https://github.com/Charles40130/Projet-Dev-Web-Ing1#readme"
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 2.3 MiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 26 KiB |
@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-cloud-sun-rain-icon lucide-cloud-sun-rain"><path d="M12 2v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="M20 12h2"/><path d="m19.07 4.93-1.41 1.41"/><path d="M15.947 12.65a4 4 0 0 0-5.925-4.128"/><path d="M3 20a5 5 0 1 1 8.9-4H13a3 3 0 0 1 2 5.24"/><path d="M11 20v2"/><path d="M7 19v2"/></svg>
|
||||
|
Before Width: | Height: | Size: 494 B |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 76 KiB |
@ -1,54 +0,0 @@
|
||||
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
|
||||
import { AuthProvider } from "./AuthContext.jsx";
|
||||
import Home from "./pages/Home.jsx";
|
||||
import About from "./pages/About.jsx";
|
||||
import Gestion from "./pages/Gestion/Gestion.jsx";
|
||||
import Header from "./components/Header.jsx";
|
||||
import ObjectManagement from "./pages/Gestion/ObjectManagement.jsx";
|
||||
import Objet from "./pages/Gestion/Objet.jsx";
|
||||
import AddObject from "./pages/Gestion/AddObject.jsx";
|
||||
import Signup from "./pages/Signup.jsx";
|
||||
import Login from "./pages/Login.jsx";
|
||||
import Profil from "./pages/Profil.jsx";
|
||||
import Sidebar from "./pages/Admin/sidebar.jsx";
|
||||
import User from "./pages/Admin/User.jsx";
|
||||
import Dashboard from "./pages/Admin/Dashboard.jsx";
|
||||
import AdminObjet from "./pages/Admin/AdminObjet.jsx";
|
||||
import ProtectedRoute from "./ProtectedRoute.jsx";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<AuthProvider>
|
||||
<Router>
|
||||
<div>
|
||||
<Header />
|
||||
<Routes>
|
||||
{/* Routes publiques */}
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/about" element={<About />} />
|
||||
<Route path="/signup" element={<Signup />} />
|
||||
<Route path="/login" element={<Login />} />
|
||||
|
||||
{/* Routes protégées pour tous les utilisateurs connectés */}
|
||||
<Route path="/gestion" element={<ProtectedRoute element={<Gestion />} allowedRoles={['admin', 'complexe', 'user']} />} />
|
||||
<Route path="/gestionObjets" element={<ProtectedRoute element={<ObjectManagement />} allowedRoles={['admin', 'complexe', 'user']} />} />
|
||||
<Route path="/objet" element={<ProtectedRoute element={<Objet />} allowedRoles={['admin', 'complexe', 'user']} />} />
|
||||
|
||||
{/* Routes protégées pour les admins et complexes */}
|
||||
<Route path="/ajouterObjet" element={<ProtectedRoute element={<AddObject />} allowedRoles={['admin', 'complexe']} />} />
|
||||
<Route path="/profil" element={<ProtectedRoute element={<Profil />} allowedRoles={['admin', 'complexe','user']} />} />
|
||||
|
||||
{/* Routes protégées pour tous les utilisateurs connectés */}
|
||||
<Route path="/sidebar" element={<ProtectedRoute element={<Sidebar />} allowedRoles={['admin', 'complexe', 'user']} />} />
|
||||
<Route path="/user" element={<ProtectedRoute element={<User />} allowedRoles={['admin', 'complexe', 'user']} />} />
|
||||
{/* Routes protégées pour les admins uniquement */}
|
||||
<Route path="/dashboard" element={<ProtectedRoute element={<Dashboard />} allowedRoles={['admin']} />} />
|
||||
<Route path="/adminobjet" element={<ProtectedRoute element={<AdminObjet />} allowedRoles={['admin']} />} />
|
||||
</Routes>
|
||||
</div>
|
||||
</Router>
|
||||
</AuthProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@ -1,47 +0,0 @@
|
||||
import React, { createContext, useContext, useState, useEffect } from "react";
|
||||
import { jwtDecode } from "jwt-decode";
|
||||
|
||||
|
||||
// Créer le contexte
|
||||
const AuthContext = createContext();
|
||||
|
||||
// Hook pour accéder facilement au contexte
|
||||
export const useAuth = () => useContext(AuthContext);
|
||||
|
||||
// Fournisseur de contexte qui gère l'état du token et de l'utilisateur
|
||||
export const AuthProvider = ({ children }) => {
|
||||
const [token, setToken] = useState(localStorage.getItem("token"));
|
||||
const [user, setUser] = useState(null);
|
||||
|
||||
// Met à jour le token et décode l'utilisateur
|
||||
useEffect(() => {
|
||||
if (token) {
|
||||
try {
|
||||
const decoded = jwtDecode(token);
|
||||
setUser(decoded);
|
||||
} catch (error) {
|
||||
console.error("Erreur lors du décodage du token:", error);
|
||||
setUser(null);
|
||||
}
|
||||
} else {
|
||||
setUser(null);
|
||||
}
|
||||
}, [token]);
|
||||
|
||||
const login = (newToken) => {
|
||||
localStorage.setItem("token", newToken);
|
||||
setToken(newToken);
|
||||
};
|
||||
|
||||
const logout = () => {
|
||||
localStorage.removeItem("token");
|
||||
setToken(null);
|
||||
setUser(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={{ token, user, login, logout }}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
};
|
||||
@ -1,20 +0,0 @@
|
||||
import React from "react";
|
||||
import { Navigate } from "react-router-dom";
|
||||
import { useAuth } from "./AuthContext"; // Utilisation du contexte d'authentification
|
||||
|
||||
function ProtectedRoute({ element, allowedRoles }) {
|
||||
const { token, user } = useAuth(); // Vérifier si un token existe, donc si l'utilisateur est authentifié
|
||||
|
||||
// Si l'utilisateur n'est pas authentifié, redirigez-le vers la page de login
|
||||
if (!token) {
|
||||
return <Navigate to="/login" />;
|
||||
}
|
||||
if(user){
|
||||
if (allowedRoles && !allowedRoles.includes(user?.role)) {
|
||||
return <Navigate to="/" />;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
}
|
||||
|
||||
export default ProtectedRoute;
|
||||
@ -1,33 +0,0 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { Check, X } from "lucide-react";
|
||||
import { useAuth } from "../AuthContext";
|
||||
|
||||
function Alert({ affAlert, setAffAlert, message }) {
|
||||
const { user } = useAuth();
|
||||
|
||||
useEffect(() => {
|
||||
if (affAlert) {
|
||||
const timer = setTimeout(() => {
|
||||
setAffAlert(false);
|
||||
}, 3000);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [affAlert, setAffAlert]);
|
||||
|
||||
return (
|
||||
affAlert && user?.role !== "user" && (
|
||||
<div className="fixed top-6 right-4 z-50 bg-slate-700 text-white px-6 py-4 rounded-xl shadow-lg flex items-center gap-4 w-[90%] sm:w-[350px]">
|
||||
<Check className="text-green-400 w-6 h-6 flex-shrink-0" />
|
||||
<p className="text-sm sm:text-base flex-grow">{message}</p>
|
||||
<button
|
||||
onClick={() => setAffAlert(false)}
|
||||
className="text-white hover:text-gray-300"
|
||||
>
|
||||
<X />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default Alert;
|
||||
@ -1,23 +0,0 @@
|
||||
import React, {useState} from "react";
|
||||
import { TriangleAlert,X } from "lucide-react";
|
||||
import { useAuth } from "../AuthContext";
|
||||
|
||||
function AlertInactive({affAlert,setAffAlert, message}) {
|
||||
const { user } = useAuth();
|
||||
return (
|
||||
(affAlert&&(user?.role!=="user")&&(
|
||||
<div className="fixed z-50 flex flex-col md:flex-row bg-slate-600 w-full md:w-1/2 lg:w-1/3 top-20 right-1 sm:right-4 rounded-lg p-4 md:p-5 items-center gap-4 md:gap-6 shadow-lg opacity-90">
|
||||
<button onClick={()=>setAffAlert(false)}className="absolute top-2 right-2 text-white hover:text-gray-300">
|
||||
<X/>
|
||||
</button>
|
||||
|
||||
<TriangleAlert className="text-red-700 w-12 h-12 md:w-16 md:h-16" />
|
||||
|
||||
<p className="text-sm md:text-base text-white text-center md:text-left">
|
||||
{message}
|
||||
</p>
|
||||
</div>
|
||||
)));
|
||||
}
|
||||
|
||||
export default AlertInactive;
|
||||
@ -1,29 +0,0 @@
|
||||
import React from "react";
|
||||
import { Battery } from "lucide-react";
|
||||
import Progress from "react-circle-progress-bar";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function BatterieInfo({ object }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div key={object.id} className="bg-white p-6 rounded-xl min-w-5xl">
|
||||
<div className="flex align-items gap-6">
|
||||
<div className="w-12 h-12 bg-indigo-100 rounded-lg flex items-center justify-center mb-4">
|
||||
<Battery className="text-indigo-600" size={24} />
|
||||
</div>
|
||||
<h1 className="text-black text-2xl font-bold mb-1 ">
|
||||
{t('components.batterieInfo.title')}
|
||||
</h1>
|
||||
</div>
|
||||
<div className="flex flex-col items-center">
|
||||
<Progress progress={object.batterie} />
|
||||
<h1 className="font-bold">
|
||||
{t('components.batterieInfo.batteryType')}{" "}
|
||||
<span className="capitalize font-normal">{object.type_batterie}</span>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default BatterieInfo;
|
||||
@ -1,25 +0,0 @@
|
||||
import React from "react";
|
||||
import { ChartLine } from "lucide-react";
|
||||
|
||||
function BoutonGraphique({ type, setGraphStates, graphStates, graphCible }) {
|
||||
const handleClick = () => {
|
||||
setGraphStates((prev) => ({ ...prev, [type]: !prev[type] }));
|
||||
};
|
||||
return !graphStates[type] ? (
|
||||
<button
|
||||
className="bg-blue-200 py-2 my-2 px-4 rounded-full mr-2"
|
||||
onClick={handleClick}
|
||||
>
|
||||
<ChartLine className="text-indigo-600" size={24} />
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className="bg-blue-400 py-2 my-2 px-4 rounded-full mr-2"
|
||||
onClick={handleClick}
|
||||
>
|
||||
<ChartLine className="text-indigo-600" size={24} />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default BoutonGraphique;
|
||||
@ -1,280 +0,0 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { BadgePlus } from "lucide-react";
|
||||
import axios from "axios";
|
||||
import { API_BASE_URL } from "../config";
|
||||
import { useAuth } from "../AuthContext";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function FormNewObject({ isAdmin }) {
|
||||
const { t } = useTranslation();
|
||||
const { user } = useAuth();
|
||||
const [categorie, setCategorie] = useState();
|
||||
const [description, setDescription] = useState("");
|
||||
const [type, setType] = useState("");
|
||||
const [location, setLocalisation] = useState("");
|
||||
const [proprio_id, setProprio_id] = useState(user?.id);
|
||||
const [batterieType, setBatterieType] = useState("");
|
||||
const [status, setStatus] = useState("active");
|
||||
const [nom, setNom] = useState("");
|
||||
const [Response, setResponse] = useState(null);
|
||||
const [isActive, setActive] = useState(true);
|
||||
const [verif, setVerif] = useState(false);
|
||||
const [enregistre, setEnregistre] = useState(false);
|
||||
const [messRequete, setMessRequete] = useState("");
|
||||
function handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (verif) {
|
||||
console.log("Envoi requete");
|
||||
axios
|
||||
.post(`${API_BASE_URL}/addObject`, {
|
||||
nom,
|
||||
description,
|
||||
type,
|
||||
location,
|
||||
status,
|
||||
batterieType,
|
||||
proprio_id,
|
||||
})
|
||||
.then((response) => {
|
||||
setMessRequete(t('components.formNewObject.successRecord'));
|
||||
setEnregistre(true);
|
||||
console.log("Ajout de l'objet réussit :", response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
setMessRequete(t('components.formNewObject.errorRecord'));
|
||||
console.error("Erreur lors de l'ajout de l'objet :", error);
|
||||
});
|
||||
setVerif(false);
|
||||
resetForm();
|
||||
} else {
|
||||
setVerif(true);
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
axios
|
||||
.get(`${API_BASE_URL}/getCategories`)
|
||||
.then((response) => {
|
||||
if (response.data.length === 0) {
|
||||
console.warn(t('components.formNewObject.noCategory'));
|
||||
} else {
|
||||
setCategorie(response.data);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Erreur lors de la récupération des catégories :", error);
|
||||
});
|
||||
}, []);
|
||||
function resetForm() {
|
||||
setNom("");
|
||||
setStatus("active");
|
||||
setDescription("");
|
||||
setType("");
|
||||
setLocalisation("");
|
||||
setBatterieType("");
|
||||
if (isAdmin) set_id("");
|
||||
setActive(true);
|
||||
}
|
||||
function handleCancel() {
|
||||
if (verif) {
|
||||
setVerif(false);
|
||||
} else {
|
||||
resetForm();
|
||||
}
|
||||
}
|
||||
function handleStatusChange() {
|
||||
setActive((prevIsActive) => {
|
||||
const newIsActive = !prevIsActive;
|
||||
setStatus(newIsActive ? "active" : "inactive");
|
||||
return newIsActive;
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className="bg-white p-6 rounded-xl min-w-5xl">
|
||||
<div className="flex align-items gap-9">
|
||||
{isAdmin ? (
|
||||
<h2 className="text-2xl font-semibold mb-3">
|
||||
{t('components.formNewObject.addTitle')}
|
||||
</h2>
|
||||
) : (
|
||||
<>
|
||||
<div className="w-12 h-12 bg-indigo-100 rounded-lg flex items-center justify-center mb-4">
|
||||
<BadgePlus className="text-indigo-600" size={24} />
|
||||
</div>
|
||||
<h1 className="text-black text-2xl font-bold mb-1">
|
||||
{!verif
|
||||
? t('components.formNewObject.enterData')
|
||||
: t('components.formNewObject.confirmData')}
|
||||
</h1>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="mb-5">
|
||||
<label
|
||||
htmlFor="nom"
|
||||
className="block mb-2 text-sm font-medium text-gray-900"
|
||||
>
|
||||
{t('components.formNewObject.name')}
|
||||
</label>
|
||||
<input
|
||||
id="nom"
|
||||
className="text-gray-600 border rounded-lg p-2 w-full"
|
||||
type="text"
|
||||
value={nom}
|
||||
onChange={(e) => setNom(e.target.value)}
|
||||
required
|
||||
disabled={verif}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-5">
|
||||
<label
|
||||
htmlFor="description"
|
||||
className="block mb-2 text-sm font-medium text-gray-900"
|
||||
>
|
||||
{t('components.formNewObject.description')}
|
||||
</label>
|
||||
<input
|
||||
id="description"
|
||||
className="text-gray-600 border rounded-lg p-2 w-full"
|
||||
type="text"
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
required
|
||||
disabled={verif}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-5">
|
||||
<label
|
||||
htmlFor="type"
|
||||
className="block mb-2 text-sm font-medium text-gray-900"
|
||||
>
|
||||
{t('components.formNewObject.type')}
|
||||
</label>
|
||||
<select
|
||||
id="type"
|
||||
className="text-gray-600 border rounded-lg p-2 w-full"
|
||||
value={type}
|
||||
onChange={(e) => setType(e.target.value)}
|
||||
required
|
||||
disabled={verif}
|
||||
>
|
||||
<option value="">{t('components.formNewObject.selectType')}</option>
|
||||
{categorie&&categorie.map((cat, index) => (
|
||||
<option key={index} value={cat}>
|
||||
{cat}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="mb-5">
|
||||
<label
|
||||
htmlFor="location"
|
||||
className="block mb-2 text-sm font-medium text-gray-900"
|
||||
>
|
||||
{t('components.formNewObject.location')}
|
||||
</label>
|
||||
<input
|
||||
id="location"
|
||||
className="text-gray-600 border rounded-lg p-2 w-full"
|
||||
type="text"
|
||||
value={location}
|
||||
onChange={(e) => setLocalisation(e.target.value)}
|
||||
required
|
||||
disabled={verif}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-5">
|
||||
<label
|
||||
htmlFor="batterieType"
|
||||
className="block mb-2 text-sm font-medium text-gray-900"
|
||||
>
|
||||
{t('components.formNewObject.batteryType')}
|
||||
</label>
|
||||
<input
|
||||
id="batterieType"
|
||||
className="text-gray-600 border rounded-lg p-2 w-full"
|
||||
type="text"
|
||||
value={batterieType}
|
||||
onChange={(e) => setBatterieType(e.target.value)}
|
||||
required
|
||||
disabled={verif}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-5">
|
||||
<label
|
||||
htmlFor="proprio_id"
|
||||
className="block mb-2 text-sm font-medium text-gray-900"
|
||||
>
|
||||
{t('components.formNewObject.owner')}
|
||||
</label>
|
||||
<input
|
||||
id="proprio_id"
|
||||
className="text-gray-600 border rounded-lg p-2 w-full"
|
||||
type="number"
|
||||
value={proprio_id}
|
||||
onChange={(e) => setProprio_id(e.target.value)}
|
||||
required
|
||||
disabled={verif || !isAdmin}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-5">
|
||||
<label className="block mb-2 text-sm font-medium text-gray-900">
|
||||
{t('components.formNewObject.status')}
|
||||
</label>
|
||||
<div className="inline-flex items-center gap-2">
|
||||
<label
|
||||
htmlFor="switch-component-on"
|
||||
className="text-slate-600 text-sm cursor-pointer"
|
||||
>
|
||||
{t('components.formNewObject.inactive')}
|
||||
</label>
|
||||
<div className="relative inline-block w-11 h-5">
|
||||
<input
|
||||
id="switch-component-on"
|
||||
type="checkbox"
|
||||
checked={isActive}
|
||||
onChange={handleStatusChange}
|
||||
className="peer appearance-none w-11 h-5 bg-slate-100 rounded-full checked:bg-slate-800 cursor-pointer transition-colors duration-300"
|
||||
disabled={verif}
|
||||
/>
|
||||
<label
|
||||
htmlFor="switch-component-on"
|
||||
className="absolute top-0 left-0 w-5 h-5 bg-white rounded-full border border-slate-300 shadow-sm transition-transform duration-300 peer-checked:translate-x-6 peer-checked:border-slate-800 cursor-pointer"
|
||||
></label>
|
||||
</div>
|
||||
<label
|
||||
htmlFor="switch-component-on"
|
||||
className="text-slate-600 text-sm cursor-pointer"
|
||||
>
|
||||
{t('components.formNewObject.active')}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col mb-5 ">
|
||||
<button
|
||||
type={"submit"}
|
||||
className="text-blue-500 hover:cursor-pointer hover:underline mb-2"
|
||||
>
|
||||
{!verif ? t('components.formNewObject.confirmInfos') : t('components.formNewObject.sureBtn')}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="text-red-500 hover:cursor-pointer hover:underline"
|
||||
onClick={handleCancel}
|
||||
>
|
||||
{!verif ? t('components.formNewObject.deleteInfos') : t('components.formNewObject.changeBtn')}
|
||||
</button>
|
||||
</div>
|
||||
<p className={enregistre ? "text-green-700" : "text-red-700"}>
|
||||
{messRequete}
|
||||
</p>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
export default FormNewObject;
|
||||
@ -1,230 +0,0 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { X, Menu, LogIn, UserPlus, LogOut, User } from "lucide-react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useAuth } from "../AuthContext";
|
||||
import LanguageSwitcher from "./LanguageSwitcher";
|
||||
|
||||
function Header() {
|
||||
const { t } = useTranslation();
|
||||
const { token, user, logout } = useAuth();
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const [showAdminDropdown, setShowAdminDropdown] = useState(false);
|
||||
|
||||
const toggleAdminDropdown = () => {
|
||||
setShowAdminDropdown((prev) => !prev);
|
||||
};
|
||||
|
||||
return (
|
||||
<header className="bg-white shadow-md relative">
|
||||
<div className="mx-auto px-4 sm:px-6 lg:px-8 py-4 flex justify-between items-center sm:grid sm:grid-cols-3">
|
||||
{/* Logo Section */}
|
||||
<div className="flex justify-start">
|
||||
<Link to="/" className="text-2xl font-bold text-indigo-600">
|
||||
VigiMétéo
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Navigation Section (Centered on Desktop) */}
|
||||
<nav
|
||||
className={`${
|
||||
isMenuOpen ? "block" : "hidden"
|
||||
} absolute z-[1000] top-16 left-0 w-full bg-white shadow-md sm:static sm:flex sm:justify-center sm:shadow-none sm:bg-transparent`}
|
||||
>
|
||||
<ul className="flex flex-col sm:flex-row gap-4 sm:gap-10 text-gray-600 p-4 sm:p-0 font-medium">
|
||||
<li>
|
||||
<Link
|
||||
to="/"
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
className="text-gray-600 hover:text-indigo-600 transition-colors"
|
||||
>
|
||||
{t('header.home')}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
to="/about"
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
className="text-gray-600 hover:text-indigo-600 transition-colors"
|
||||
>
|
||||
{t('header.about')}
|
||||
</Link>
|
||||
</li>
|
||||
|
||||
{/* Logic for roles */}
|
||||
{token && (
|
||||
<>
|
||||
{user?.role === "user" ? (
|
||||
<li>
|
||||
<Link
|
||||
to="/gestionObjets"
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
className="text-gray-600 hover:text-indigo-600 transition-colors"
|
||||
>
|
||||
{t('header.visualizer')}
|
||||
</Link>
|
||||
</li>
|
||||
) : (
|
||||
<li>
|
||||
<Link
|
||||
to="/gestion"
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
className="text-gray-600 hover:text-indigo-600 transition-colors"
|
||||
>
|
||||
{t('header.manage')}
|
||||
</Link>
|
||||
</li>
|
||||
)}
|
||||
{user?.role === "admin" && (
|
||||
<li className="relative group">
|
||||
<button
|
||||
onClick={toggleAdminDropdown}
|
||||
className="flex items-center text-gray-600 hover:text-indigo-600 focus:outline-none transition-colors"
|
||||
>
|
||||
{t('header.admin')}
|
||||
<svg
|
||||
className="ml-1 h-4 w-4 fill-current"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" />
|
||||
</svg>
|
||||
</button>
|
||||
{showAdminDropdown && (
|
||||
<div className="absolute top-full left-0 mt-2 w-48 bg-white border border-gray-200 rounded-md shadow-lg z-50">
|
||||
<Link
|
||||
to="/dashboard"
|
||||
className="block px-4 py-2 text-gray-700 hover:bg-gray-100"
|
||||
onClick={() => {setShowAdminDropdown(false);setIsMenuOpen(false);}}
|
||||
>
|
||||
{t('header.dashboard')}
|
||||
</Link>
|
||||
<Link
|
||||
to="/user"
|
||||
className="block px-4 py-2 text-gray-700 hover:bg-gray-100"
|
||||
onClick={() => {setShowAdminDropdown(false);setIsMenuOpen(false);}}
|
||||
>
|
||||
{t('header.manageUsers')}
|
||||
</Link>
|
||||
<Link
|
||||
to="/adminobjet"
|
||||
className="block px-4 py-2 text-gray-700 hover:bg-gray-100"
|
||||
onClick={() => {setShowAdminDropdown(false);setIsMenuOpen(false);}}
|
||||
>
|
||||
{t('header.manageObjects')}
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</li>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Mobile-only auth links */}
|
||||
{!token ? (
|
||||
<>
|
||||
<li className="sm:hidden">
|
||||
<Link
|
||||
to="/login"
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
className="hover:text-indigo-600 flex items-center gap-2"
|
||||
>
|
||||
<LogIn size={20} />
|
||||
{t('header.login')}
|
||||
</Link>
|
||||
</li>
|
||||
<li className="sm:hidden">
|
||||
<Link
|
||||
to="/signup"
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
className="flex items-center gap-2 bg-indigo-600 text-white px-4 py-2 rounded-lg"
|
||||
>
|
||||
<UserPlus size={20} />
|
||||
{t('header.signup')}
|
||||
</Link>
|
||||
</li>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<li className="sm:hidden">
|
||||
<Link
|
||||
to="/profil"
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
className="flex items-center gap-2 text-gray-600 hover:text-indigo-600"
|
||||
>
|
||||
<User size={20} />
|
||||
<span>{t('header.profile')}</span>
|
||||
</Link>
|
||||
</li>
|
||||
<li className="sm:hidden">
|
||||
<button
|
||||
onClick={() => {
|
||||
logout();
|
||||
setIsMenuOpen(false);
|
||||
}}
|
||||
className="flex items-center gap-2 text-gray-600 hover:text-red-600 w-full text-left"
|
||||
>
|
||||
<LogOut size={20} />
|
||||
<span>{t('header.logout')}</span>
|
||||
</button>
|
||||
</li>
|
||||
</>
|
||||
)}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
{/* Auth Section (Right side) */}
|
||||
<div className="flex justify-end items-center gap-4">
|
||||
{!token ? (
|
||||
<div className="hidden sm:flex gap-4 items-center">
|
||||
<LanguageSwitcher />
|
||||
<Link
|
||||
to="/login"
|
||||
className="text-gray-600 hover:text-indigo-600 flex items-center gap-2 transition-colors"
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
>
|
||||
<LogIn size={20} />
|
||||
{t('header.login')}
|
||||
</Link>
|
||||
<Link
|
||||
to="/signup"
|
||||
className="flex items-center gap-2 bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700 transition-colors shadow-sm"
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
>
|
||||
<UserPlus size={20} />
|
||||
{t('header.signup')}
|
||||
</Link>
|
||||
</div>
|
||||
) : (
|
||||
<div className="hidden sm:flex items-center gap-6">
|
||||
<LanguageSwitcher />
|
||||
<Link
|
||||
to="/profil"
|
||||
className="flex items-center gap-2 text-gray-600 hover:text-indigo-600 transition-colors"
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
>
|
||||
<User size={20} />
|
||||
</Link>
|
||||
<button
|
||||
onClick={logout}
|
||||
className="flex items-center gap-2 text-gray-600 hover:text-red-600 transition-colors"
|
||||
>
|
||||
<LogOut size={20} />
|
||||
<span className="font-medium">{t('header.logout')}</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Mobile Menu Button */}
|
||||
<button
|
||||
className="sm:hidden text-gray-600 hover:text-indigo-600 focus:outline-none"
|
||||
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||
>
|
||||
{isMenuOpen ? <X size={24} /> : <Menu size={24} />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
export default Header;
|
||||
@ -1,52 +0,0 @@
|
||||
import React from "react";
|
||||
import { Info } from "lucide-react";
|
||||
import { useAuth } from "../AuthContext";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function InfoObject({ object,defafficherModif }) {
|
||||
const { t } = useTranslation();
|
||||
const {user} = useAuth();
|
||||
return (
|
||||
<div key={object.id} className="bg-white p-6 rounded-xl min-w-5xl">
|
||||
<div className="flex align-items gap-6 mb-6">
|
||||
<div className="w-12 h-12 bg-indigo-100 rounded-lg flex items-center justify-center mb-1">
|
||||
<Info className="text-indigo-600" size={24} />
|
||||
</div>
|
||||
<h1 className="text-black text-2xl font-bold mb-1 ">{t('components.infoObject.title')}</h1>
|
||||
</div>
|
||||
<div className="mb-5">
|
||||
<p className="text-black-900 font-bold">{t('components.infoObject.description')}</p>
|
||||
<p className="text-gray-600 capitalize">{object.description}</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-5">
|
||||
<p className="text-black-900 font-bold">{t('components.infoObject.type')}</p>
|
||||
<p className="text-gray-600 capitalize">{object.type}</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-5">
|
||||
<p className="text-black-900 font-bold">{t('components.infoObject.location')}</p>
|
||||
<p className="text-gray-600">{object.location}</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-5">
|
||||
<p className="text-black-900 font-bold">{t('components.infoObject.status')}</p>
|
||||
<p className="text-gray-600 capitalize">{object.status}</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-5">
|
||||
<p className="text-black-900 font-bold">
|
||||
{t('components.infoObject.lastUpdate')}
|
||||
</p>
|
||||
<p className="text-gray-600">{object.last_update}</p>
|
||||
</div>
|
||||
{user?.role!=="user"&&(
|
||||
<div className="flex items-center gap-4 mb-1">
|
||||
<a className="text-blue-500 hover:cursor-pointer" onClick={(()=>defafficherModif(true))}>{t('components.infoObject.modify')}</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default InfoObject;
|
||||
@ -1,27 +0,0 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function LanguageSwitcher() {
|
||||
const { i18n } = useTranslation();
|
||||
|
||||
const changeLanguage = (lng) => {
|
||||
i18n.changeLanguage(lng);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex space-x-2">
|
||||
<button
|
||||
onClick={() => changeLanguage('fr')}
|
||||
className={`px-2 py-1 text-sm rounded transition-colors ${i18n.resolvedLanguage === 'fr' ? 'bg-blue-600 text-white font-bold' : 'bg-gray-200 text-gray-700 hover:bg-gray-300'}`}
|
||||
>
|
||||
FR
|
||||
</button>
|
||||
<button
|
||||
onClick={() => changeLanguage('en')}
|
||||
className={`px-2 py-1 text-sm rounded transition-colors ${i18n.resolvedLanguage === 'en' ? 'bg-blue-600 text-white font-bold' : 'bg-gray-200 text-gray-700 hover:bg-gray-300'}`}
|
||||
>
|
||||
EN
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,98 +0,0 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import {
|
||||
LineChart,
|
||||
Line,
|
||||
XAxis,
|
||||
YAxis,
|
||||
CartesianGrid,
|
||||
Tooltip,
|
||||
Legend,
|
||||
ResponsiveContainer,
|
||||
ReferenceLine,
|
||||
} from "recharts";
|
||||
|
||||
import { Wind } from "lucide-react";
|
||||
import axios from "axios";
|
||||
import { API_BASE_URL } from "../config";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function MeteoGraph({ object, categorie, Logo,reference}) {
|
||||
const { t } = useTranslation();
|
||||
const [rawData, setRawData] = useState([]);
|
||||
const identifiant = object.id;
|
||||
useEffect(() => {
|
||||
axios.get(`${API_BASE_URL}/meteo?id=${identifiant}`).then((response) => {
|
||||
setRawData(response.data);
|
||||
});
|
||||
}, [object]);
|
||||
useEffect(() => {
|
||||
if (reference?.current) {
|
||||
reference.current.scrollIntoView({ behavior: "smooth" });
|
||||
}
|
||||
}, [reference]);
|
||||
function getAvg() {
|
||||
let moyenne = 0;
|
||||
rawData.forEach((element) => {
|
||||
if(element){
|
||||
moyenne += element[categorie];
|
||||
}
|
||||
});
|
||||
return moyenne / rawData.length;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
ref={reference}
|
||||
key={object.id}
|
||||
className="bg-white mb-6 p-6 rounded-xl min-w-5xl"
|
||||
style={{ width: "100%", height: "400px" }}
|
||||
|
||||
>
|
||||
<div className="flex align-items gap-6">
|
||||
<div className="w-12 h-12 bg-indigo-100 rounded-lg flex items-center justify-center mb-4">
|
||||
<Logo className="text-indigo-600" size={24} />
|
||||
</div>
|
||||
{categorie === "temperature" ? (
|
||||
<h1 className="text-black text-2xl font-bold mb-1 ">
|
||||
{t('components.meteoGraph.historyTemp')}
|
||||
</h1>
|
||||
) : categorie === "humidity" ? (
|
||||
<h1 className="text-black text-2xl font-bold mb-1 ">
|
||||
{t('components.meteoGraph.historyHum')}
|
||||
</h1>
|
||||
) : (
|
||||
<h1 className="text-black text-2xl font-bold mb-1 ">
|
||||
{t('components.meteoGraph.historyPres')}
|
||||
</h1>
|
||||
)}
|
||||
</div>
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<LineChart
|
||||
width={500}
|
||||
height={300}
|
||||
data={rawData}
|
||||
margin={{
|
||||
top: 5,
|
||||
right: 30,
|
||||
left: 10,
|
||||
bottom: 60,
|
||||
}}
|
||||
>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="timestamp" />
|
||||
<YAxis />
|
||||
<Tooltip />
|
||||
<Legend />
|
||||
<Line
|
||||
type="monotone"
|
||||
dataKey={categorie}
|
||||
stroke="#8884d8"
|
||||
activeDot={{ r: 8 }}
|
||||
/>
|
||||
<ReferenceLine y={getAvg()} label={t('components.meteoGraph.average')} stroke="red" />
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MeteoGraph;
|
||||
@ -1,84 +0,0 @@
|
||||
import { Thermometer, Sun, CircleGauge, Droplet } from "lucide-react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import axios from "axios";
|
||||
import { API_BASE_URL } from "../config";
|
||||
import BoutonGraphique from "./BoutonGraphique";
|
||||
import AlertInactive from "./AlertInactive";
|
||||
import ParticularMeteo from "./ParticularMeteo";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function MeteoInfos({ object, graphStates, setGraphStates, graphRefs }) {
|
||||
const { t } = useTranslation();
|
||||
const [rawData, setRawData] = useState([]);
|
||||
const [AffAlert, setAffAlert] = useState(false);
|
||||
const [AffRegles, setAffRegles] = useState(false);
|
||||
const identifiant = object.id;
|
||||
|
||||
useEffect(() => {
|
||||
axios.get(`${API_BASE_URL}/meteo?id=${identifiant}`).then((response) => {
|
||||
setRawData(response.data);
|
||||
if (rawData.length < 5) {
|
||||
setAffAlert(true);
|
||||
}
|
||||
});
|
||||
}, [object]);
|
||||
|
||||
const lastData = rawData.length > 0 ? rawData[rawData.length - 1] : null;
|
||||
return (
|
||||
<div key={object.id} className="bg-white p-6 rounded-xl min-w-5xl">
|
||||
{AffAlert && object.status === "active" && (
|
||||
<AlertInactive affAlert={AffAlert} setAffAlert={setAffAlert} message={t('components.meteoInfos.alertInactive')}
|
||||
/>
|
||||
)}
|
||||
<div className="flex align-items gap-6">
|
||||
<div className="w-12 h-12 bg-indigo-100 rounded-lg flex items-center justify-center mb-4">
|
||||
<Sun className="text-indigo-600" size={24} />
|
||||
</div>
|
||||
<h1 className="text-black text-2xl font-bold mb-1 ">{t('components.meteoInfos.currentWeather')}</h1>
|
||||
</div>
|
||||
{lastData ? (
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<ParticularMeteo
|
||||
type="temperature"
|
||||
data={lastData}
|
||||
Icon={Thermometer}
|
||||
texte1={t('components.meteoInfos.temperature')}
|
||||
texte2="°C"
|
||||
graphStates={graphStates}
|
||||
setGraphStates={setGraphStates}
|
||||
graphRefs={graphRefs}
|
||||
/>
|
||||
|
||||
<ParticularMeteo
|
||||
type="pressure"
|
||||
data={lastData}
|
||||
Icon={CircleGauge}
|
||||
texte1={t('components.meteoInfos.pressure')}
|
||||
texte2="hPa"
|
||||
graphStates={graphStates}
|
||||
setGraphStates={setGraphStates}
|
||||
graphRefs={graphRefs}
|
||||
/>
|
||||
|
||||
<ParticularMeteo
|
||||
type="humidity"
|
||||
data={lastData}
|
||||
Icon={Droplet}
|
||||
texte1={t('components.meteoInfos.humidity')}
|
||||
texte2="%"
|
||||
graphStates={graphStates}
|
||||
setGraphStates={setGraphStates}
|
||||
graphRefs={graphRefs}
|
||||
/>
|
||||
<h1 className="text-gray-500 text-sm">
|
||||
{t('components.meteoInfos.lastRecord')} {lastData.timestamp}
|
||||
</h1>
|
||||
</div>
|
||||
) : (
|
||||
<p>{t('components.meteoInfos.loading')}</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MeteoInfos;
|
||||
@ -1,164 +0,0 @@
|
||||
import React, { useState } from "react";
|
||||
import { Info } from "lucide-react";
|
||||
import axios from "axios";
|
||||
import { API_BASE_URL } from "../config";
|
||||
import {useAuth} from "../AuthContext";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function ModifObject({ object, defafficherModif }) {
|
||||
const { t } = useTranslation();
|
||||
const {user}=useAuth();
|
||||
const [description, setDescription] = useState(object.description || "");
|
||||
const [type, setType] = useState(object.type || "");
|
||||
const [location, setLocalisation] = useState(object.location || "");
|
||||
const [status, setStatus] = useState(object.status || "inactive");
|
||||
const [isActive, setActive] = useState(object.status === "active");
|
||||
|
||||
function handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
axios
|
||||
.post(`${API_BASE_URL}/modifObjet`, {
|
||||
id: object.id,
|
||||
idUser:user.id,
|
||||
description,
|
||||
type,
|
||||
location,
|
||||
status,
|
||||
shouldUpdatePoints:true
|
||||
})
|
||||
.then((response) => {
|
||||
console.log("Modification réussie :", response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Erreur lors de la modification :", error);
|
||||
});
|
||||
defafficherModif(false);
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
defafficherModif(false);
|
||||
}
|
||||
|
||||
function handleStatusChange() {
|
||||
setActive((prevIsActive) => {
|
||||
const newIsActive = !prevIsActive;
|
||||
setStatus(newIsActive ? "active" : "inactive");
|
||||
return newIsActive;
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className="bg-white p-6 rounded-xl min-w-5xl">
|
||||
<div className="flex align-items gap-9">
|
||||
<div className="w-12 h-12 bg-indigo-100 rounded-lg flex items-center justify-center mb-4">
|
||||
<Info className="text-indigo-600" size={24} />
|
||||
</div>
|
||||
<h1 className="text-black text-2xl font-bold mb-1">
|
||||
{t('components.modifObject.title')}
|
||||
</h1>
|
||||
</div>
|
||||
<div className="mb-5">
|
||||
<label
|
||||
htmlFor="description"
|
||||
className="block mb-2 text-sm font-medium text-gray-900"
|
||||
>
|
||||
{t('components.modifObject.description')}
|
||||
</label>
|
||||
<input
|
||||
id="description"
|
||||
className="text-gray-600 border rounded-lg p-2 w-full"
|
||||
type="text"
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-5">
|
||||
<label
|
||||
htmlFor="type"
|
||||
className="block mb-2 text-sm font-medium text-gray-900"
|
||||
>
|
||||
{t('components.modifObject.type')}
|
||||
</label>
|
||||
<input
|
||||
id="type"
|
||||
className="text-gray-600 border rounded-lg p-2 w-full"
|
||||
type="text"
|
||||
value={type}
|
||||
onChange={(e) => setType(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-5">
|
||||
<label
|
||||
htmlFor="location"
|
||||
className="block mb-2 text-sm font-medium text-gray-900"
|
||||
>
|
||||
{t('components.modifObject.location')}
|
||||
</label>
|
||||
<input
|
||||
id="location"
|
||||
className="text-gray-600 border rounded-lg p-2 w-full"
|
||||
type="text"
|
||||
value={location}
|
||||
onChange={(e) => setLocalisation(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-5">
|
||||
<label className="block mb-2 text-sm font-medium text-gray-900">
|
||||
{t('components.modifObject.status')}
|
||||
</label>
|
||||
<div className="inline-flex items-center gap-2">
|
||||
<label
|
||||
htmlFor="switch-component-on"
|
||||
className="text-slate-600 text-sm cursor-pointer"
|
||||
>
|
||||
{t('components.modifObject.inactive')}
|
||||
</label>
|
||||
<div className="relative inline-block w-11 h-5">
|
||||
<input
|
||||
id="switch-component-on"
|
||||
type="checkbox"
|
||||
checked={isActive}
|
||||
onChange={handleStatusChange}
|
||||
className="peer appearance-none w-11 h-5 bg-slate-100 rounded-full checked:bg-slate-800 cursor-pointer transition-colors duration-300"
|
||||
/>
|
||||
<label
|
||||
htmlFor="switch-component-on"
|
||||
className="absolute top-0 left-0 w-5 h-5 bg-white rounded-full border border-slate-300 shadow-sm transition-transform duration-300 peer-checked:translate-x-6 peer-checked:border-slate-800 cursor-pointer"
|
||||
></label>
|
||||
</div>
|
||||
<label
|
||||
htmlFor="switch-component-on"
|
||||
className="text-slate-600 text-sm cursor-pointer"
|
||||
>
|
||||
{t('components.modifObject.active')}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-5 flex flex-col">
|
||||
<button
|
||||
type="submit"
|
||||
className="text-blue-500 hover:cursor-pointer hover:underline"
|
||||
>
|
||||
{t('components.modifObject.confirmMods')}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="text-red-500 hover:cursor-pointer hover:underline"
|
||||
onClick={handleCancel}
|
||||
>
|
||||
{t('components.modifObject.cancelMods')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
export default ModifObject;
|
||||
@ -1,170 +0,0 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import BoutonGraphique from "./BoutonGraphique";
|
||||
import { Bell } from "lucide-react";
|
||||
import Slider from "@mui/material/Slider";
|
||||
import { API_BASE_URL } from "../config";
|
||||
import axios from "axios";
|
||||
import { useAuth } from "../AuthContext";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const identifiant = new URLSearchParams(window.location.search).get("id");
|
||||
function ParticularMeteo({
|
||||
type,
|
||||
data,
|
||||
Icon,
|
||||
texte1,
|
||||
texte2,
|
||||
graphStates,
|
||||
setGraphStates,
|
||||
graphRefs,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const {user} = useAuth();
|
||||
const [affRegles, setAffRegles] = useState(false);
|
||||
const [rangeValue, setRangeValue] = useState([0, 0]);
|
||||
const [alerteActive, setAlerteActive] = useState(false);
|
||||
const minMaxValues = {
|
||||
temperature: [-50, 60],
|
||||
pressure: [940, 1060],
|
||||
humidity: [10, 100],
|
||||
};
|
||||
const MIN = minMaxValues[type][0];
|
||||
const MAX = minMaxValues[type][1];
|
||||
const formatLabel = (value) => {
|
||||
switch (type) {
|
||||
case "temperature":
|
||||
return `${value}°C`;
|
||||
case "pressure":
|
||||
return `${value} hPa`;
|
||||
case "humidity":
|
||||
return `${value}%`;
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
};
|
||||
const marks = [
|
||||
{
|
||||
value: MIN,
|
||||
label: formatLabel(MIN),
|
||||
},
|
||||
{
|
||||
value: MAX,
|
||||
label: formatLabel(MAX),
|
||||
},
|
||||
];
|
||||
useEffect(() => {
|
||||
axios.get(`${API_BASE_URL}/getRange?id=${identifiant}`).then((response) => {
|
||||
setRangeValue([
|
||||
response.data[0][type + "_min"],
|
||||
response.data[0][type + "_max"],
|
||||
]);
|
||||
});
|
||||
}, [identifiant]);
|
||||
const color =
|
||||
rangeValue[0] > data[type] || rangeValue[1] < data[type]
|
||||
? "text-red-600"
|
||||
: "text-indigo-600";
|
||||
|
||||
const defRangeData = () => {
|
||||
console.log("Données envoyées :", {
|
||||
id: identifiant,
|
||||
min: rangeValue[0],
|
||||
max: rangeValue[1],
|
||||
type,
|
||||
});
|
||||
axios
|
||||
.post(`${API_BASE_URL}/modifRangeData`, {
|
||||
id: identifiant,
|
||||
idUser:user.id,
|
||||
min: parseFloat(rangeValue[0]),
|
||||
max: parseFloat(rangeValue[1]),
|
||||
type,
|
||||
})
|
||||
.then((response) => {
|
||||
console.log("Modification réussie :", response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Erreur lors de la modification :", error);
|
||||
});
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
const handleChange = (event, newValue) => {
|
||||
setRangeValue(newValue);
|
||||
};
|
||||
function valuetext(value) {
|
||||
return `${value}°C`;
|
||||
}
|
||||
if (data[type]) {
|
||||
return (
|
||||
<div className="bg-indigo-50 flex flex-col rounded-lg items-center w-full">
|
||||
<div className="flex align-items gap-3 w-full justify-between">
|
||||
<div className="flex align-items ml-3 gap-2">
|
||||
<div className="flex items-center">
|
||||
<Icon className={`${color}`} size={40} />
|
||||
</div>
|
||||
<div className="flex flex-col items-start">
|
||||
<h1 className={`${color} text-xl font-bold `}>{texte1}</h1>
|
||||
<h1 className={`${color} text-4xl font-bold`}>
|
||||
{Math.round(data[type])}{" "}
|
||||
<span className="text-lg">{texte2}</span>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
{user?.role!=="user" && (
|
||||
<button
|
||||
onClick={() => {
|
||||
setAffRegles(!affRegles);
|
||||
}}
|
||||
>
|
||||
<Bell
|
||||
className={`${color} hover:pointer-events-auto`}
|
||||
size={30}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
<BoutonGraphique
|
||||
type={type}
|
||||
graphStates={graphStates}
|
||||
setGraphStates={setGraphStates}
|
||||
graphCible={graphRefs[type]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{affRegles && (
|
||||
<div className="p-6">
|
||||
<h1 className="text-red-500 text-l font-semibold">
|
||||
{t('components.particularMeteo.defineLimit')}
|
||||
</h1>
|
||||
<div className="p-4">
|
||||
<Slider
|
||||
getAriaLabel={() => "Temperature range"}
|
||||
value={rangeValue}
|
||||
onChange={handleChange}
|
||||
valueLabelDisplay="auto"
|
||||
min={MIN}
|
||||
max={MAX}
|
||||
marks={marks}
|
||||
getAriaValueText={valuetext}
|
||||
disableSwap
|
||||
/>
|
||||
</div>
|
||||
{color=="text-red-600" &&(
|
||||
<p className="text-red-500 text-l mb-2">{t('components.particularMeteo.outOfBounds')}</p>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => defRangeData()}
|
||||
className="text-blue-700 hover:text-white border border-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center me-2 mb-2 dark:border-blue-500 dark:text-blue-500 dark:hover:text-white dark:hover:bg-blue-500 dark:focus:ring-blue-800"
|
||||
>
|
||||
{t('components.particularMeteo.setAlert')}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ParticularMeteo;
|
||||
@ -1,51 +0,0 @@
|
||||
import React,{useEffect, useState} from "react";
|
||||
import { User } from "lucide-react";
|
||||
import axios from "axios";
|
||||
import { API_BASE_URL } from "../config";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function UserInfosObject({ user}) {
|
||||
const { t } = useTranslation();
|
||||
const [userInfo,setuserInfo]=useState({});
|
||||
useEffect(()=>{
|
||||
console.log(user);
|
||||
axios
|
||||
.post(`${API_BASE_URL}/publicUser`, {
|
||||
id: user,
|
||||
})
|
||||
.then((response) => {
|
||||
setuserInfo(response.data);
|
||||
console.log("Modification réussie :", response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Erreur lors de la modification :", error);
|
||||
});
|
||||
},[user]);
|
||||
|
||||
return (
|
||||
<div className="bg-white p-6 rounded-xl min-w-5xl">
|
||||
<div className="flex align-items gap-6 mb-6">
|
||||
<div className="w-12 h-12 bg-indigo-100 rounded-lg flex items-center justify-center mb-1">
|
||||
<User className="text-indigo-600" size={24} />
|
||||
</div>
|
||||
<h1 className="text-black text-2xl font-bold mb-1 ">{t('components.userInfosObject.title')}</h1>
|
||||
</div>
|
||||
<div className="mb-5">
|
||||
<p className="text-black-900 font-bold">{t('components.userInfosObject.pseudo')}</p>
|
||||
<p className="text-gray-600 capitalize">{userInfo.pseudo}</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-5">
|
||||
<p className="text-black-900 font-bold">{t('components.userInfosObject.gender')}</p>
|
||||
<p className="text-gray-600 capitalize">{userInfo.gender}</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-5">
|
||||
<p className="text-black-900 font-bold">{t('components.userInfosObject.points')}</p>
|
||||
<p className="text-gray-600">{userInfo.points}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default UserInfosObject;
|
||||
@ -1,69 +0,0 @@
|
||||
import React, { useEffect, useState} from "react";
|
||||
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
|
||||
|
||||
import { Wind } from "lucide-react";
|
||||
import axios from "axios";
|
||||
import { API_BASE_URL } from "../config";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function WindGraph({ object,reference }) {
|
||||
const { t } = useTranslation();
|
||||
const [rawData, setRawData] = useState([]);
|
||||
const identifiant = object.id;
|
||||
useEffect(() => {
|
||||
axios.get(`${API_BASE_URL}/wind?id=${identifiant}`).then((response) => {
|
||||
setRawData(response.data);
|
||||
});
|
||||
}, [object]);
|
||||
const CustomTooltip = ({ payload, label, active }) => {
|
||||
if (active && payload && payload.length) {
|
||||
const { wind_speed, timestamp,wind_direction } = payload[0].payload;
|
||||
return (
|
||||
<div className="custom-tooltip">
|
||||
<p><strong>{t('components.windGraph.date')}</strong> {timestamp}</p>
|
||||
<p><strong>{t('components.windGraph.windSpeed')}</strong> {wind_speed} km/h</p>
|
||||
<p><strong>{t('components.windGraph.windDirection')}</strong> {wind_direction}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
useEffect(() => {
|
||||
if (reference?.current) {
|
||||
reference.current.scrollIntoView({ behavior: "smooth" });
|
||||
}
|
||||
}, [reference]);
|
||||
return (
|
||||
<div key={object.id} ref={reference} className="bg-white mb-6 p-6 rounded-xl min-w-5xl" style={{width: "100%", height: "400px"}}>
|
||||
<div className="flex align-items gap-6">
|
||||
<div className="w-12 h-12 bg-indigo-100 rounded-lg flex items-center justify-center mb-4">
|
||||
<Wind className="text-indigo-600" size={24} />
|
||||
</div>
|
||||
<h1 className="text-black text-2xl font-bold mb-1 ">{t('components.windGraph.title')}</h1>
|
||||
</div>
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<LineChart
|
||||
width={500}
|
||||
height={300}
|
||||
data={rawData}
|
||||
margin={{
|
||||
top: 5,
|
||||
right: 30,
|
||||
left: 10,
|
||||
bottom: 60,
|
||||
}}
|
||||
>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="timestamp" />
|
||||
<YAxis />
|
||||
<Tooltip content={<CustomTooltip/>}/>
|
||||
<Legend />
|
||||
<Line type="monotone" dataKey="wind_speed" stroke="#8884d8" activeDot={{ r: 8 }} />
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default WindGraph;
|
||||
@ -1,76 +0,0 @@
|
||||
import { Wind } from "lucide-react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import axios from "axios";
|
||||
import { API_BASE_URL } from "../config";
|
||||
import BoutonGraphique from "./BoutonGraphique";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function WindInfo({ object, setGraphStates, graphStates, graphRefs, reference}) {
|
||||
const { t } = useTranslation();
|
||||
const [rawData, setRawData] = useState([]);
|
||||
const identifiant = object.id;
|
||||
useEffect(() => {
|
||||
axios.get(`${API_BASE_URL}/wind?id=${identifiant}`).then((response) => {
|
||||
setRawData(response.data);
|
||||
});
|
||||
}, [object]);
|
||||
useEffect(() => {
|
||||
if (reference?.current) {
|
||||
reference.current.scrollIntoView({ behavior: "smooth" });
|
||||
}
|
||||
}, [reference]);
|
||||
|
||||
const lastData = rawData.length > 0 ? rawData[rawData.length - 1] : null;
|
||||
|
||||
return (
|
||||
<div key={object.id} className="bg-white p-6 rounded-xl min-w-5xl">
|
||||
<div className="flex align-items gap-6">
|
||||
<div className="w-12 h-12 bg-indigo-100 rounded-lg flex items-center justify-center mb-1">
|
||||
<Wind className="text-indigo-600" size={24} />
|
||||
</div>
|
||||
<h1 className="text-black text-2xl font-bold">{t('components.windInfo.currentWind')}</h1>
|
||||
</div>
|
||||
{lastData ? (
|
||||
<div className="flex flex-col items-center gap-1">
|
||||
<img
|
||||
src={`.//img/${lastData.wind_direction}.png`}
|
||||
alt="Wind Direction"
|
||||
className="h-25"
|
||||
/>
|
||||
<h1 className="text-gray-600 text-xl font-bold mb-1 ">
|
||||
{lastData.wind_direction}
|
||||
</h1>
|
||||
<div className="bg-indigo-50 rounded-lg flex flex-col items-center mb-1 w-full">
|
||||
<div className="flex align-items gap-3 w-full justify-between">
|
||||
<div className="flex align-items ml-3 gap-2">
|
||||
<div className="flex items-center">
|
||||
<Wind className="text-indigo-600" size={40} />
|
||||
</div>
|
||||
<div className="flex flex-col items-start">
|
||||
<h1 className="text-indigo-600 text-xl font-bold ">{t('components.windInfo.value')}</h1>
|
||||
<h1 className="text-gray-700 text-4xl font-bold">
|
||||
{lastData.wind_speed} <span className="text-lg">Km/h</span>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
<BoutonGraphique
|
||||
type="wind"
|
||||
graphStates={graphStates}
|
||||
setGraphStates={setGraphStates}
|
||||
graphCible={graphRefs.wind}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<h1 className="text-gray-500 text-sm">
|
||||
{t('components.windInfo.lastRecord')} {lastData.timestamp}
|
||||
</h1>
|
||||
</div>
|
||||
) : (
|
||||
<p>{t('components.windInfo.loading')}</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default WindInfo;
|
||||
@ -1 +0,0 @@
|
||||
export const API_BASE_URL = 'http://localhost:8888';
|
||||
@ -1,24 +0,0 @@
|
||||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
|
||||
import frTranslation from './locales/fr.json';
|
||||
import enTranslation from './locales/en.json';
|
||||
|
||||
const resources = {
|
||||
fr: { translation: frTranslation },
|
||||
en: { translation: enTranslation }
|
||||
};
|
||||
|
||||
i18n
|
||||
.use(LanguageDetector)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
resources,
|
||||
fallbackLng: 'fr',
|
||||
interpolation: {
|
||||
escapeValue: false
|
||||
}
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
@ -1,3 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||