Merge pull request 'Initial' (#1) from master into main
Reviewed-on: #1
This commit is contained in:
35
.gitignore
vendored
Normal file
35
.gitignore
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
HELP.md
|
||||
target/
|
||||
testfiles/
|
||||
jte-classes/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
BIN
.mvn/wrapper/maven-wrapper.jar
vendored
Normal file
BIN
.mvn/wrapper/maven-wrapper.jar
vendored
Normal file
Binary file not shown.
2
.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
2
.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip
|
||||
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar
|
||||
316
mvnw
vendored
Executable file
316
mvnw
vendored
Executable file
@@ -0,0 +1,316 @@
|
||||
#!/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
|
||||
#
|
||||
# https://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.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Maven Start Up Batch script
|
||||
#
|
||||
# Required ENV vars:
|
||||
# ------------------
|
||||
# JAVA_HOME - location of a JDK home dir
|
||||
#
|
||||
# Optional ENV vars
|
||||
# -----------------
|
||||
# M2_HOME - location of maven2's installed home dir
|
||||
# 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
|
||||
export JAVA_HOME="`/usr/libexec/java_home`"
|
||||
else
|
||||
export JAVA_HOME="/Library/Java/Home"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
if [ -r /etc/gentoo-release ] ; then
|
||||
JAVA_HOME=`java-config --jre-home`
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$M2_HOME" ] ; then
|
||||
## resolve links - $0 may be a link to maven's home
|
||||
PRG="$0"
|
||||
|
||||
# need this for relative symlinks
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG="`dirname "$PRG"`/$link"
|
||||
fi
|
||||
done
|
||||
|
||||
saveddir=`pwd`
|
||||
|
||||
M2_HOME=`dirname "$PRG"`/..
|
||||
|
||||
# make it fully qualified
|
||||
M2_HOME=`cd "$M2_HOME" && pwd`
|
||||
|
||||
cd "$saveddir"
|
||||
# echo Using m2 at $M2_HOME
|
||||
fi
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||
if $cygwin ; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --unix "$M2_HOME"`
|
||||
[ -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 "$M2_HOME" ] &&
|
||||
M2_HOME="`(cd "$M2_HOME"; pwd)`"
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME="`(cd "$JAVA_HOME"; 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; \\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
|
||||
|
||||
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
|
||||
|
||||
# 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/.."; pwd`
|
||||
fi
|
||||
# end of workaround
|
||||
done
|
||||
echo "${basedir}"
|
||||
}
|
||||
|
||||
# concatenates all lines of a file
|
||||
concat_lines() {
|
||||
if [ -f "$1" ]; then
|
||||
echo "$(tr -s '\n' ' ' < "$1")"
|
||||
fi
|
||||
}
|
||||
|
||||
BASE_DIR=`find_maven_basedir "$(pwd)"`
|
||||
if [ -z "$BASE_DIR" ]; then
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
##########################################################################################
|
||||
# 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.
|
||||
##########################################################################################
|
||||
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found .mvn/wrapper/maven-wrapper.jar"
|
||||
fi
|
||||
else
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
|
||||
fi
|
||||
if [ -n "$MVNW_REPOURL" ]; then
|
||||
jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
|
||||
else
|
||||
jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
|
||||
fi
|
||||
while IFS="=" read key value; do
|
||||
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
|
||||
esac
|
||||
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Downloading from: $jarUrl"
|
||||
fi
|
||||
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
|
||||
if $cygwin; then
|
||||
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
|
||||
fi
|
||||
|
||||
if command -v wget > /dev/null; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found wget ... using wget"
|
||||
fi
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||
else
|
||||
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||
fi
|
||||
elif command -v curl > /dev/null; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found curl ... using curl"
|
||||
fi
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
curl -o "$wrapperJarPath" "$jarUrl" -f
|
||||
else
|
||||
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
|
||||
fi
|
||||
|
||||
else
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Falling back to using Java to download"
|
||||
fi
|
||||
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
|
||||
# For Cygwin, switch paths to Windows format before running javac
|
||||
if $cygwin; then
|
||||
javaClass=`cygpath --path --windows "$javaClass"`
|
||||
fi
|
||||
if [ -e "$javaClass" ]; then
|
||||
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo " - Compiling MavenWrapperDownloader.java ..."
|
||||
fi
|
||||
# Compiling the Java class
|
||||
("$JAVA_HOME/bin/javac" "$javaClass")
|
||||
fi
|
||||
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||
# Running the downloader
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo " - Running MavenWrapperDownloader.java ..."
|
||||
fi
|
||||
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
##########################################################################################
|
||||
# End of extension
|
||||
##########################################################################################
|
||||
|
||||
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo $MAVEN_PROJECTBASEDIR
|
||||
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 "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --path --windows "$M2_HOME"`
|
||||
[ -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
|
||||
|
||||
exec "$JAVACMD" \
|
||||
$MAVEN_OPTS \
|
||||
$MAVEN_DEBUG_OPTS \
|
||||
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||
"-Dmaven.home=${M2_HOME}" \
|
||||
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
|
||||
188
mvnw.cmd
vendored
Normal file
188
mvnw.cmd
vendored
Normal file
@@ -0,0 +1,188 @@
|
||||
@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 https://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 Maven Start Up Batch script
|
||||
@REM
|
||||
@REM Required ENV vars:
|
||||
@REM JAVA_HOME - location of a JDK home dir
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM M2_HOME - location of maven2's installed home dir
|
||||
@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 DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
|
||||
|
||||
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||
IF "%%A"=="wrapperUrl" SET DOWNLOAD_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 DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
|
||||
)
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Couldn't find %WRAPPER_JAR%, downloading it ...
|
||||
echo Downloading from: %DOWNLOAD_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('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
|
||||
"}"
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Finished downloading %WRAPPER_JAR%
|
||||
)
|
||||
)
|
||||
@REM End of extension
|
||||
|
||||
@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%
|
||||
218
pom.xml
Normal file
218
pom.xml
Normal file
@@ -0,0 +1,218 @@
|
||||
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.7.5</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>com.github.russp</groupId>
|
||||
<artifactId>bpe</artifactId>
|
||||
<version>0.0.2-SNAPSHOT</version>
|
||||
<name>bpe</name>
|
||||
<description>Bad-practice editor</description>
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<jte.version>3.0.3</jte.version>
|
||||
<repackage.classifier/>
|
||||
<spring-native.version>0.12.1</spring-native.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.experimental</groupId>
|
||||
<artifactId>spring-native</artifactId>
|
||||
<version>${spring-native.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.11.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>gg.jte</groupId>
|
||||
<artifactId>jte</artifactId>
|
||||
<version>${jte.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>gg.jte</groupId>
|
||||
<artifactId>jte-spring-boot-starter-2</artifactId>
|
||||
<version>${jte.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
<classifier>${repackage.classifier}</classifier>
|
||||
<image>
|
||||
<builder>paketobuildpacks/builder:tiny</builder>
|
||||
<env>
|
||||
<BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
|
||||
</env>
|
||||
</image>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.experimental</groupId>
|
||||
<artifactId>spring-aot-maven-plugin</artifactId>
|
||||
<version>${spring-native.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test-generate</id>
|
||||
<goals>
|
||||
<goal>test-generate</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>generate</id>
|
||||
<goals>
|
||||
<goal>generate</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>gg.jte</groupId>
|
||||
<artifactId>jte-maven-plugin</artifactId>
|
||||
<version>${jte.version}</version>
|
||||
<configuration>
|
||||
<sourceDirectory>${basedir}/src/main/jte</sourceDirectory>
|
||||
<contentType>Html</contentType>
|
||||
<targetResourceDirectory>${project.build.directory}/classes</targetResourceDirectory>
|
||||
<extensions>
|
||||
<extension>
|
||||
<className>gg.jte.nativeimage.NativeResourcesExtension</className>
|
||||
</extension>
|
||||
</extensions>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>jte-gen</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>generate</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>gg.jte</groupId>
|
||||
<artifactId>jte-native-resources</artifactId>
|
||||
<version>${jte.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-releases</id>
|
||||
<name>Spring Releases</name>
|
||||
<url>https://repo.spring.io/release</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spring-releases</id>
|
||||
<name>Spring Releases</name>
|
||||
<url>https://repo.spring.io/release</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>native</id>
|
||||
<properties>
|
||||
<repackage.classifier>exec</repackage.classifier>
|
||||
<native-buildtools.version>0.9.13</native-buildtools.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit.platform</groupId>
|
||||
<artifactId>junit-platform-launcher</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.graalvm.buildtools</groupId>
|
||||
<artifactId>native-maven-plugin</artifactId>
|
||||
<version>${native-buildtools.version}</version>
|
||||
<extensions>true</extensions>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test-native</id>
|
||||
<phase>test</phase>
|
||||
<goals>
|
||||
<goal>test</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>build-native</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>build</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
13
src/main/java/com/github/russp/bpe/BpeApplication.java
Normal file
13
src/main/java/com/github/russp/bpe/BpeApplication.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.github.russp.bpe;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class BpeApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(BpeApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.github.russp.bpe.config;
|
||||
|
||||
import gg.jte.springframework.boot.autoconfigure.ReactiveJteAutoConfiguration;
|
||||
import gg.jte.springframework.boot.autoconfigure.ReactiveJteViewResolver;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.Locale;
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@ImportAutoConfiguration(ReactiveJteAutoConfiguration.class)
|
||||
@RequiredArgsConstructor
|
||||
public class JteConfiguration {
|
||||
|
||||
private final ReactiveJteViewResolver jteViewResolver;
|
||||
|
||||
@PostConstruct
|
||||
public void vr() {
|
||||
log.info("ReactiveJteViewResolver created: {}", jteViewResolver);
|
||||
jteViewResolver.resolveViewName("login", Locale.ENGLISH)
|
||||
.log()
|
||||
.block();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.github.russp.bpe.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;
|
||||
|
||||
@EnableWebFluxSecurity
|
||||
public class SecurityConfig {
|
||||
|
||||
@Value("${app.username}")
|
||||
private String username;
|
||||
@Value("${app.password}")
|
||||
private String password;
|
||||
|
||||
@Bean
|
||||
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
|
||||
return http.authorizeExchange()
|
||||
.pathMatchers("/login", "/*.css", "/*.js").permitAll()
|
||||
.anyExchange().authenticated()
|
||||
.and().formLogin().loginPage("/login")
|
||||
.and().logout()
|
||||
.requiresLogout(ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, "/logout"))
|
||||
.and()
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MapReactiveUserDetailsService userDetailsService() {
|
||||
UserDetails user = User
|
||||
.withUsername(username)
|
||||
.password(passwordEncoder().encode(password))
|
||||
.roles("USER")
|
||||
.build();
|
||||
password = "";
|
||||
return new MapReactiveUserDetailsService(user);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.github.russp.bpe.http;
|
||||
|
||||
import com.github.russp.bpe.service.FilesService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.multipart.FilePart;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.FormatStyle;
|
||||
import java.util.ArrayList;
|
||||
|
||||
@Slf4j
|
||||
@Controller
|
||||
@RequiredArgsConstructor
|
||||
public class BrowserController {
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);
|
||||
|
||||
private final FilesService filesService;
|
||||
|
||||
@GetMapping("/")
|
||||
public String list(@RequestParam(defaultValue = "/") String path, Model model) {
|
||||
log.info("Path: {}", path);
|
||||
|
||||
var files = filesService.ls(path)
|
||||
.map(file -> FileDto.builder()
|
||||
.dir(file.isDir())
|
||||
.name(file.getName())
|
||||
.fullName(file.getFullName())
|
||||
.size(String.valueOf(file.getSize()))
|
||||
.modifiedAt(file.getModifiedAt().format(DATE_TIME_FORMATTER))
|
||||
.build())
|
||||
.collectList();
|
||||
|
||||
var breadcrumbs = filesService.pwd(path).map(file -> FileDto.builder()
|
||||
.name(file.getName())
|
||||
.fullName(file.getFullName())
|
||||
.build())
|
||||
.collect(
|
||||
ArrayList::new,
|
||||
(list, value) -> list.add(0, value)
|
||||
);
|
||||
|
||||
model.addAttribute("files", files);
|
||||
model.addAttribute("breadcrumbs", breadcrumbs);
|
||||
|
||||
return "browse";
|
||||
}
|
||||
|
||||
@GetMapping("edit")
|
||||
public String edit(@RequestParam String path, Model model) {
|
||||
var breadcrumbs = filesService.parent(path).map(file -> FileDto.builder()
|
||||
.name(file.getName())
|
||||
.fullName(file.getFullName())
|
||||
.build())
|
||||
.collect(
|
||||
ArrayList::new,
|
||||
(list, value) -> list.add(0, value)
|
||||
);
|
||||
model.addAttribute("breadcrumbs", breadcrumbs);
|
||||
model.addAttribute("selectedFile", path);
|
||||
|
||||
model.addAttribute("selectedFileName", Paths.get(path).getFileName().toString());
|
||||
return "edit";
|
||||
}
|
||||
|
||||
@GetMapping(value = "/content",
|
||||
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
||||
public @ResponseBody Flux<DataBuffer> content(String path) {
|
||||
return filesService.content(path);
|
||||
}
|
||||
|
||||
@PostMapping(value = "/content")
|
||||
public @ResponseBody Mono<Void> save(@RequestParam String path,
|
||||
@RequestPart("file") Mono<FilePart> filePartMono) {
|
||||
return filePartMono
|
||||
.flatMap(fp -> filesService.save(path, fp::transferTo))
|
||||
.then(Mono.empty());
|
||||
}
|
||||
}
|
||||
21
src/main/java/com/github/russp/bpe/http/FileDto.java
Normal file
21
src/main/java/com/github/russp/bpe/http/FileDto.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package com.github.russp.bpe.http;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FileDto {
|
||||
private String name;
|
||||
private String size;
|
||||
private String modifiedAt;
|
||||
private boolean dir;
|
||||
|
||||
private String fullName;
|
||||
}
|
||||
16
src/main/java/com/github/russp/bpe/http/LoginController.java
Normal file
16
src/main/java/com/github/russp/bpe/http/LoginController.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package com.github.russp.bpe.http;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/login")
|
||||
public class LoginController {
|
||||
|
||||
@GetMapping
|
||||
public String login() {
|
||||
return "login";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.github.russp.bpe.http;
|
||||
|
||||
import org.springframework.security.web.reactive.result.view.CsrfRequestDataValueProcessor;
|
||||
import org.springframework.security.web.server.csrf.CsrfToken;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@ControllerAdvice
|
||||
public class SecurityControllerAdvice {
|
||||
|
||||
@ModelAttribute(CsrfRequestDataValueProcessor.DEFAULT_CSRF_ATTR_NAME)
|
||||
Mono<CsrfToken> csrfToken(ServerWebExchange exchange) {
|
||||
return exchange.getAttribute(CsrfToken.class.getName());
|
||||
}
|
||||
|
||||
}
|
||||
27
src/main/java/com/github/russp/bpe/model/FileEntry.java
Normal file
27
src/main/java/com/github/russp/bpe/model/FileEntry.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package com.github.russp.bpe.model;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public class FileEntry implements Comparable<FileEntry> {
|
||||
private final boolean dir;
|
||||
private final String name;
|
||||
private final String fullName;
|
||||
private final long size;
|
||||
private final LocalDateTime modifiedAt;
|
||||
|
||||
@Override
|
||||
public int compareTo(FileEntry o) {
|
||||
if (dir && !o.isDir()) {
|
||||
return -1;
|
||||
}
|
||||
if (!dir && o.isDir()) {
|
||||
return 1;
|
||||
}
|
||||
return name.compareTo(o.getName());
|
||||
}
|
||||
}
|
||||
126
src/main/java/com/github/russp/bpe/service/FilesService.java
Normal file
126
src/main/java/com/github/russp/bpe/service/FilesService.java
Normal file
@@ -0,0 +1,126 @@
|
||||
package com.github.russp.bpe.service;
|
||||
|
||||
import com.github.russp.bpe.model.FileEntry;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class FilesService {
|
||||
|
||||
@Value("${app.fs.root}")
|
||||
private String root;
|
||||
|
||||
public Flux<FileEntry> ls(String path) {
|
||||
try {
|
||||
var rootPath = Paths.get(root);
|
||||
return Flux.fromStream(Files.list(Path.of(root, path))
|
||||
.map(p -> FileEntry.builder()
|
||||
.name(p.getFileName().toString())
|
||||
.fullName(p.toString().replace(root, ""))
|
||||
.dir(Files.isDirectory(p))
|
||||
.modifiedAt(getLastModifiedTime(p))
|
||||
.size(size(p))
|
||||
.build()
|
||||
)
|
||||
.sorted()
|
||||
);
|
||||
} catch (IOException e) {
|
||||
log.debug("Listing error for path {}: {}", path, e.getMessage());
|
||||
return Flux.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Flux<FileEntry> parent(String path) {
|
||||
var parent = Path.of(path).getParent();
|
||||
if (parent == null) {
|
||||
return Flux.empty();
|
||||
}
|
||||
return pwd(Path.of(path).getParent().toString());
|
||||
}
|
||||
|
||||
public Flux<FileEntry> pwd(String path) {
|
||||
if (!StringUtils.hasText(path)) {
|
||||
return Flux.empty();
|
||||
}
|
||||
var rootPath = Path.of(root);
|
||||
var end = Path.of(root, path);
|
||||
|
||||
return Flux.<Path, Path>generate(() -> end, (state, sink) -> {
|
||||
if (rootPath.equals(state)) {
|
||||
sink.complete();
|
||||
}
|
||||
sink.next(state);
|
||||
return state.getParent();
|
||||
})
|
||||
.map(p -> FileEntry.builder()
|
||||
.name(p.getFileName().toString())
|
||||
.fullName(p.toString().replace(root, ""))
|
||||
.dir(Files.isDirectory(p))
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
public Flux<DataBuffer> content(String path) {
|
||||
var file = Path.of(root, path);
|
||||
return DataBufferUtils.read(file, DefaultDataBufferFactory.sharedInstance, 1024, StandardOpenOption.READ);
|
||||
}
|
||||
|
||||
public Mono<Void> save(String path, Function<Path, Mono<Void>> transferTo) {
|
||||
return Mono.just(Path.of(root, path))
|
||||
.doOnNext(fn -> {
|
||||
var file = fn.toFile();
|
||||
try {
|
||||
FileUtils.moveFile(file, new File(fn.toString() + ".bak"), StandardCopyOption.REPLACE_EXISTING);
|
||||
} catch (IOException e) {
|
||||
log.error("Can not create backup file for {}", path, e);
|
||||
// throw new RuntimeException(e);
|
||||
}
|
||||
})
|
||||
.flatMap(transferTo);
|
||||
}
|
||||
|
||||
private static LocalDateTime getLastModifiedTime(Path path) {
|
||||
try {
|
||||
return LocalDateTime.ofInstant(
|
||||
Files.getLastModifiedTime(path).toInstant(),
|
||||
ZoneId.systemDefault());
|
||||
} catch (IOException e) {
|
||||
return LocalDateTime.MIN;
|
||||
}
|
||||
}
|
||||
|
||||
private static long size(Path path) {
|
||||
try {
|
||||
if (Files.isRegularFile(path)) {
|
||||
return Files.size(path);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.debug("Size error for {}: {}", path, e.getMessage());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
0
src/main/jte/.jteroot
Normal file
0
src/main/jte/.jteroot
Normal file
104
src/main/jte/browse.jte
Normal file
104
src/main/jte/browse.jte
Normal file
@@ -0,0 +1,104 @@
|
||||
@import java.util.List
|
||||
@import com.github.russp.bpe.http.FileDto
|
||||
|
||||
@param List<FileDto> files
|
||||
@param List<FileDto> breadcrumbs
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Browser">
|
||||
|
||||
<title>Index of </title>
|
||||
|
||||
<link rel="shortcut icon" href="https://picocss.com/favicon.ico">
|
||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.4/css/all.css"
|
||||
integrity="sha384-DyZ88mC6Up2uqS4h/KRgHuoeGwBcD4Ng9SiP4dIRy0EXTlnuz47vAwmeGwVChigm" crossorigin="anonymous"/>
|
||||
|
||||
<!-- Pico.css -->
|
||||
<link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css">
|
||||
<!-- Custom template-->
|
||||
<link rel="stylesheet" href="/stylesheet.css">
|
||||
|
||||
<style></style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<header class="container">
|
||||
<h5>Browse</h5>
|
||||
<nav>
|
||||
<ul class="breadcrumbs">
|
||||
<li><a href="/"><i class="fa fa-home"></i></a></li>
|
||||
@for(FileDto breadcrumb : breadcrumbs)
|
||||
<li><a href="/?path=${breadcrumb.getFullName()}">${breadcrumb.getName()}</a></li>
|
||||
@endfor
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<!-- Main -->
|
||||
<main class="container">
|
||||
<!-- Tables -->
|
||||
<section id="tables">
|
||||
<figure>
|
||||
<table role="grid">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Date</th>
|
||||
<th scope="col">Size</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for(FileDto file : files)
|
||||
<tr>
|
||||
<th scope="row">
|
||||
@if(file.isDir())
|
||||
<a href="/?path=${file.getFullName()}">
|
||||
<i class="fa fa-folder"></i>
|
||||
${file.getName()}
|
||||
</a>
|
||||
@else
|
||||
<a href="/edit?path=${file.getFullName()}">
|
||||
<i class="fa fa-file"></i>
|
||||
${file.getName()}
|
||||
</a>
|
||||
@endif
|
||||
</th>
|
||||
<td>${file.getModifiedAt()}</td>
|
||||
<td>${file.getSize()}</td>
|
||||
</tr>
|
||||
@endfor
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
</section><!-- ./ Tables -->
|
||||
|
||||
</main><!-- ./ Main -->
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="container">
|
||||
|
||||
<details role="list">
|
||||
<summary aria-haspopup="listbox" role="button" class="secondary">Theme</summary>
|
||||
<ul role="listbox">
|
||||
<li><a href="#" data-theme-switcher="auto">Auto</a></li>
|
||||
<li><a href="#" data-theme-switcher="light">Light</a></li>
|
||||
<li><a href="#" data-theme-switcher="dark">Dark</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
<small>Built with <a href="https://picocss.com">Pico</a> • <a
|
||||
href="https://github.com/picocss/examples/blob/master/basic-template/">Source code</a></small>
|
||||
</footer><!-- ./ Footer -->
|
||||
|
||||
|
||||
<!-- Minimal theme switcher -->
|
||||
<script src="../js/minimal-theme-switcher.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
147
src/main/jte/edit.jte
Normal file
147
src/main/jte/edit.jte
Normal file
@@ -0,0 +1,147 @@
|
||||
@import java.util.List
|
||||
@import org.springframework.security.web.server.csrf.CsrfToken
|
||||
@import com.github.russp.bpe.http.FileDto
|
||||
|
||||
@param String selectedFile
|
||||
@param String selectedFileName
|
||||
@param List<FileDto> breadcrumbs
|
||||
|
||||
@param CsrfToken _csrf
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Browser">
|
||||
|
||||
<meta name="_csrf" content="${_csrf.getToken()}"/>
|
||||
<meta name="_csrf_header" content="${_csrf.getHeaderName()}"/>
|
||||
|
||||
<title>Edit ${selectedFileName}</title>
|
||||
|
||||
<link rel="shortcut icon" href="https://picocss.com/favicon.ico">
|
||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.4/css/all.css"
|
||||
integrity="sha384-DyZ88mC6Up2uqS4h/KRgHuoeGwBcD4Ng9SiP4dIRy0EXTlnuz47vAwmeGwVChigm" crossorigin="anonymous"/>
|
||||
|
||||
<!-- Pico.css -->
|
||||
<link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css">
|
||||
<!-- Custom template-->
|
||||
<link rel="stylesheet" href="/stylesheet.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<header class="container">
|
||||
<h5>Edit ${selectedFileName}</h5>
|
||||
<nav>
|
||||
<ul class="breadcrumbs">
|
||||
<li><a href="/"><i class="fa fa-home"></i></a></li>
|
||||
@for(FileDto breadcrumb : breadcrumbs)
|
||||
<li><a href="/?path=${breadcrumb.getFullName()}">${breadcrumb.getName()}</a></li>
|
||||
@endfor
|
||||
<li>${selectedFileName}</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="javascript:save()" role="button">Save</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<!-- Main -->
|
||||
<main class="container">
|
||||
<section id="editor">
|
||||
<figure>
|
||||
<div id="editor"></div>
|
||||
</figure>
|
||||
</section>
|
||||
|
||||
</main><!-- ./ Main -->
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="container">
|
||||
<small>Built with <a href="https://picocss.com">Pico</a> • <a
|
||||
href="https://github.com/picocss/examples/blob/master/basic-template/">Source code</a></small>
|
||||
</footer><!-- ./ Footer -->
|
||||
|
||||
|
||||
<!-- Minimal theme switcher -->
|
||||
<script src="../js/minimal-theme-switcher.js"></script>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.12.5/ace.js"
|
||||
integrity="sha512-gLQA+KKlMRzGRNhdvGX+3F5UHojWkIIKvG2lNQk0ZM5QUbdG17/hDobp06zXMthrJrd4U1+boOEACntTGlPjJQ=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.12.5/ext-modelist.min.js"
|
||||
integrity="sha512-gluOZJrbb4P8hFk1M2HREivZlXwZd0Uf1i5LLt6NRHjjnu7+4eE/yaYH/m82kMTU557+Q2xJhMmqtzMBBh8o/g=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/3.6.2/fetch.min.js"
|
||||
integrity="sha512-1Gn7//DzfuF67BGkg97Oc6jPN6hqxuZXnaTpC9P5uw8C6W4yUNj5hoS/APga4g1nO2X6USBb/rXtGzADdaVDeA=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css">
|
||||
|
||||
<script>
|
||||
var modelist = require("ace/ext/modelist");
|
||||
var mode = modelist.getModeForPath("${selectedFileName}");
|
||||
var editor = ace.edit("editor",
|
||||
{
|
||||
mode: mode.mode,
|
||||
selectionStyle: "text",
|
||||
theme: "ace/theme/dracula"
|
||||
});
|
||||
document.getElementById('editor').style.fontSize = '1.2em';
|
||||
|
||||
fetch('/content?path=${selectedFile}')
|
||||
.then(function (response) {
|
||||
return response.text()
|
||||
})
|
||||
.then(function (body) {
|
||||
editor.setValue(body);
|
||||
editor.clearSelection();
|
||||
editor.gotoLine(1);
|
||||
});
|
||||
|
||||
function save() {
|
||||
var blob = new Blob([editor.getValue()]);
|
||||
var formData = new FormData();
|
||||
formData.set("file", blob);
|
||||
fetch('/content?path=${selectedFile}', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
"${_csrf.getHeaderName()}": "${_csrf.getToken()}"
|
||||
},
|
||||
body: formData,
|
||||
})
|
||||
.then(function(ok) {
|
||||
Toastify({
|
||||
text: "Saved",
|
||||
duration: 3000,
|
||||
close: true,
|
||||
gravity: "top",
|
||||
position: "right",
|
||||
stopOnFocus: true,
|
||||
style: {
|
||||
background: "green",
|
||||
},
|
||||
onClick: function(){} // Callback after click
|
||||
}).showToast();
|
||||
}).catch(function(ex) {
|
||||
Toastify({
|
||||
text: "Error",
|
||||
duration: 3000,
|
||||
close: true,
|
||||
gravity: "top",
|
||||
position: "right",
|
||||
stopOnFocus: true,
|
||||
style: {
|
||||
background: "red",
|
||||
},
|
||||
onClick: function(){} // Callback after click
|
||||
}).showToast();
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/toastify-js"></script>
|
||||
</body>
|
||||
</html>
|
||||
130
src/main/jte/login.jte
Normal file
130
src/main/jte/login.jte
Normal file
@@ -0,0 +1,130 @@
|
||||
@import org.springframework.security.web.server.csrf.CsrfToken
|
||||
|
||||
@param CsrfToken _csrf
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Browser">
|
||||
|
||||
<title>Login</title>
|
||||
|
||||
<link rel="shortcut icon" href="https://picocss.com/favicon.ico">
|
||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.4/css/all.css"
|
||||
integrity="sha384-DyZ88mC6Up2uqS4h/KRgHuoeGwBcD4Ng9SiP4dIRy0EXTlnuz47vAwmeGwVChigm" crossorigin="anonymous"/>
|
||||
|
||||
<!-- Pico.css -->
|
||||
<link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css">
|
||||
<!-- Custom template-->
|
||||
<!-- <link rel="stylesheet" href="/stylesheet.css">-->
|
||||
<style>
|
||||
|
||||
/* Grid */
|
||||
body > main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
min-height: calc(100vh - 7rem);
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
article {
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
article div {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
@media (min-width: 576px) {
|
||||
body > main {
|
||||
padding: 1.25rem 0;
|
||||
}
|
||||
|
||||
article div {
|
||||
padding: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
body > main {
|
||||
padding: 1.5rem 0;
|
||||
}
|
||||
|
||||
article div {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
body > main {
|
||||
padding: 1.75rem 0;
|
||||
}
|
||||
|
||||
article div {
|
||||
padding: 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
body > main {
|
||||
padding: 2rem 0;
|
||||
}
|
||||
|
||||
article div {
|
||||
padding: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
summary[role="link"].secondary:is([aria-current],:hover,:active,:focus) {
|
||||
background-color: transparent;
|
||||
color: var(--secondary-hover);
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.grid > div:nth-of-type(2) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
body > footer {
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- Main -->
|
||||
<main class="container">
|
||||
<article class="grid">
|
||||
<div>
|
||||
<hgroup>
|
||||
<h1>Sign in</h1>
|
||||
</hgroup>
|
||||
<form method="post">
|
||||
<input type="text" name="username" placeholder="Username" aria-label="Login" autocomplete="nickname" required>
|
||||
<input type="password" name="password" placeholder="Password" aria-label="Password" autocomplete="current-password" required>
|
||||
<input type="hidden" name="${_csrf.getParameterName()}" value="${_csrf.getToken()}"/>
|
||||
<button type="submit" class="contrast">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
<div></div>
|
||||
</article>
|
||||
</main><!-- ./ Main -->
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="container">
|
||||
|
||||
<small>Built with <a href="https://picocss.com">Pico</a> • <a
|
||||
href="https://github.com/picocss/examples/blob/master/basic-template/">Source code</a></small>
|
||||
</footer><!-- ./ Footer -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
13
src/main/resources/application.yml
Normal file
13
src/main/resources/application.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
app:
|
||||
fs:
|
||||
root: ${APP_FS_ROOT:/app/fs}
|
||||
bak:
|
||||
count: 5
|
||||
username: ${APP_USERNAME:admin}
|
||||
password: ${APP_PASSWORD:admin}
|
||||
|
||||
server:
|
||||
port: 8080
|
||||
logging:
|
||||
level:
|
||||
org.springframework.web: DEBUG
|
||||
89
src/main/resources/static/js/minimal-theme-switcher.js
Normal file
89
src/main/resources/static/js/minimal-theme-switcher.js
Normal file
@@ -0,0 +1,89 @@
|
||||
/*!
|
||||
* Minimal theme switcher
|
||||
*
|
||||
* Pico.css - https://picocss.com
|
||||
* Copyright 2019-2022 - Licensed under MIT
|
||||
*/
|
||||
|
||||
const themeSwitcher = {
|
||||
|
||||
// Config
|
||||
_scheme: "auto",
|
||||
menuTarget: "details[role='list']",
|
||||
buttonsTarget: "a[data-theme-switcher]",
|
||||
buttonAttribute: "data-theme-switcher",
|
||||
rootAttribute: "data-theme",
|
||||
localStorageKey: "picoPreferedColorScheme",
|
||||
|
||||
// Init
|
||||
init() {
|
||||
this.scheme = this.schemeFromLocalStorage;
|
||||
this.initSwitchers();
|
||||
},
|
||||
|
||||
// Get color scheme from local storage
|
||||
get schemeFromLocalStorage() {
|
||||
if (typeof window.localStorage !== "undefined") {
|
||||
if (window.localStorage.getItem(this.localStorageKey) !== null) {
|
||||
return window.localStorage.getItem(this.localStorageKey);
|
||||
}
|
||||
}
|
||||
return this._scheme;
|
||||
},
|
||||
|
||||
// Prefered color scheme
|
||||
get preferedColorScheme() {
|
||||
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? "dark"
|
||||
: "light";
|
||||
},
|
||||
|
||||
// Init switchers
|
||||
initSwitchers() {
|
||||
const buttons = document.querySelectorAll(this.buttonsTarget);
|
||||
buttons.forEach((button) => {
|
||||
button.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
// Set scheme
|
||||
this.scheme = button.getAttribute(this.buttonAttribute);
|
||||
// Close dropdown
|
||||
document.querySelector(this.menuTarget).removeAttribute("open");
|
||||
}, false);
|
||||
});
|
||||
},
|
||||
|
||||
// Set scheme
|
||||
set scheme(scheme) {
|
||||
if (scheme == "auto") {
|
||||
this.preferedColorScheme == "dark"
|
||||
? (this._scheme = "dark")
|
||||
: (this._scheme = "light");
|
||||
} else if (scheme == "dark" || scheme == "light") {
|
||||
this._scheme = scheme;
|
||||
}
|
||||
this.applyScheme();
|
||||
this.schemeToLocalStorage();
|
||||
},
|
||||
|
||||
// Get scheme
|
||||
get scheme() {
|
||||
return this._scheme;
|
||||
},
|
||||
|
||||
// Apply scheme
|
||||
applyScheme() {
|
||||
document
|
||||
.querySelector("html")
|
||||
.setAttribute(this.rootAttribute, this.scheme);
|
||||
},
|
||||
|
||||
// Store scheme to local storage
|
||||
schemeToLocalStorage() {
|
||||
if (typeof window.localStorage !== "undefined") {
|
||||
window.localStorage.setItem(this.localStorageKey, this.scheme);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Init
|
||||
themeSwitcher.init();
|
||||
38
src/main/resources/static/stylesheet.css
Normal file
38
src/main/resources/static/stylesheet.css
Normal file
@@ -0,0 +1,38 @@
|
||||
/* Global CSS variables */
|
||||
:root {
|
||||
--spacing: 0.1em;
|
||||
--t-ypography-spacing-vertical: 0px;
|
||||
}
|
||||
|
||||
#editor {
|
||||
font-size: 1.2em;
|
||||
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 75vh;
|
||||
}
|
||||
|
||||
ul.breadcrumbs > li {
|
||||
padding-left: 2px;
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
ul.breadcrumbs > li:after {
|
||||
content: "/";
|
||||
text-indent: 4px;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
ul.breadcrumbs > li:last-child:after {
|
||||
content: "";
|
||||
}
|
||||
|
||||
/*body > header {*/
|
||||
/* padding-top: 0px;*/
|
||||
/* padding-bottom: 0px;*/
|
||||
/*}*/
|
||||
|
||||
/*body > main {*/
|
||||
/* padding-top: 16px;*/
|
||||
/* padding-bottom: 16px;*/
|
||||
/*}*/
|
||||
13
src/test/java/com/github/russp/bpe/BpeApplicationTests.java
Normal file
13
src/test/java/com/github/russp/bpe/BpeApplicationTests.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.github.russp.bpe;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class BpeApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user