mirror of
https://github.com/linuxserver/fleet.git
synced 2026-02-20 05:11:08 +08:00
Initial commit (of mostly all of it...)
This commit is contained in:
parent
0f2954afea
commit
ff7c510bb5
6
.gitignore
vendored
6
.gitignore
vendored
@ -21,3 +21,9 @@
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
.idea/
|
||||
build/
|
||||
out/
|
||||
*.iml
|
||||
config/fleet.properties
|
||||
BIN
.gradle/4.9/fileChanges/last-build.bin
Normal file
BIN
.gradle/4.9/fileChanges/last-build.bin
Normal file
Binary file not shown.
BIN
.gradle/4.9/fileContent/fileContent.lock
Normal file
BIN
.gradle/4.9/fileContent/fileContent.lock
Normal file
Binary file not shown.
BIN
.gradle/4.9/fileHashes/fileHashes.bin
Normal file
BIN
.gradle/4.9/fileHashes/fileHashes.bin
Normal file
Binary file not shown.
BIN
.gradle/4.9/fileHashes/fileHashes.lock
Normal file
BIN
.gradle/4.9/fileHashes/fileHashes.lock
Normal file
Binary file not shown.
BIN
.gradle/4.9/taskHistory/taskHistory.bin
Normal file
BIN
.gradle/4.9/taskHistory/taskHistory.bin
Normal file
Binary file not shown.
BIN
.gradle/4.9/taskHistory/taskHistory.lock
Normal file
BIN
.gradle/4.9/taskHistory/taskHistory.lock
Normal file
Binary file not shown.
BIN
.gradle/buildOutputCleanup/buildOutputCleanup.lock
Normal file
BIN
.gradle/buildOutputCleanup/buildOutputCleanup.lock
Normal file
Binary file not shown.
2
.gradle/buildOutputCleanup/cache.properties
Normal file
2
.gradle/buildOutputCleanup/cache.properties
Normal file
@ -0,0 +1,2 @@
|
||||
#Mon Nov 19 20:32:01 GMT 2018
|
||||
gradle.version=4.9
|
||||
BIN
.gradle/buildOutputCleanup/outputFiles.bin
Normal file
BIN
.gradle/buildOutputCleanup/outputFiles.bin
Normal file
Binary file not shown.
0
.gradle/vcsWorkingDirs/gc.properties
Normal file
0
.gradle/vcsWorkingDirs/gc.properties
Normal file
41
build.gradle
Normal file
41
build.gradle
Normal file
@ -0,0 +1,41 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
}
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
// Logging
|
||||
runtime 'org.apache.logging.log4j:log4j-api:2.11.1'
|
||||
runtime 'org.apache.logging.log4j:log4j-core:2.11.1'
|
||||
compile 'org.apache.logging.log4j:log4j-slf4j-impl:2.11.1'
|
||||
|
||||
// HTTP Framework
|
||||
compile 'com.sparkjava:spark-core:2.7.2'
|
||||
compile 'com.sparkjava:spark-template-freemarker:2.7.1'
|
||||
compile 'org.apache.httpcomponents:httpclient:4.5.7'
|
||||
|
||||
// JSON Mapping/Marshalling
|
||||
compile 'com.fasterxml.jackson.core:jackson-databind:2.9.6'
|
||||
compile 'com.fasterxml.jackson.core:jackson-core:2.9.6'
|
||||
compile 'com.fasterxml.jackson.core:jackson-annotations:2.9.6'
|
||||
|
||||
// Database
|
||||
compile 'com.zaxxer:HikariCP:3.3.1'
|
||||
compile 'org.mariadb.jdbc:mariadb-java-client:2.4.0'
|
||||
compile 'org.flywaydb:flyway-core:6.0.0-beta'
|
||||
|
||||
// MISC
|
||||
compile 'org.apache.commons:commons-lang3:3.7'
|
||||
|
||||
|
||||
// Unit Testing
|
||||
testCompile 'junit:junit:4.11'
|
||||
testCompile 'org.mockito:mockito-all:1.10.19'
|
||||
}
|
||||
21
config/example.fleet.properties
Normal file
21
config/example.fleet.properties
Normal file
@ -0,0 +1,21 @@
|
||||
# This is an example properties file for Fleet.
|
||||
# You should fill in your own values here and rename the file to "fleet.properties".
|
||||
|
||||
# Runtime
|
||||
fleet.app.port=8080
|
||||
fleet.refresh.interval=60
|
||||
|
||||
# User for management of images and repositories
|
||||
# CHANGE THESE!!
|
||||
fleet.admin.username=test
|
||||
fleet.admin.password=test
|
||||
|
||||
# Database Connectivity
|
||||
fleet.database.driver=org.mariadb.jdbc.Driver
|
||||
fleet.database.url=jdbc:mariadb://<IP_OR_URL>:3306/fleet
|
||||
fleet.database.username=<fleet_sql_user>
|
||||
fleet.database.password=<fleet_sql_password>
|
||||
|
||||
# Docker Hub
|
||||
fleet.dockerhub.username=<username_for_your_dockerhub_account>
|
||||
fleet.dockerhub.password=<password_for_your_dockerhub_account>
|
||||
BIN
config/fleet_static/logo.png
Normal file
BIN
config/fleet_static/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
172
gradlew
vendored
Executable file
172
gradlew
vendored
Executable file
@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
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
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
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
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
84
gradlew.bat
vendored
Normal file
84
gradlew.bat
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.auth;
|
||||
|
||||
public class AuthenticatedUser {
|
||||
|
||||
private final String name;
|
||||
|
||||
public AuthenticatedUser(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.auth;
|
||||
|
||||
public class AuthenticationResult {
|
||||
|
||||
private final boolean authenticated;
|
||||
private final AuthenticatedUser user;
|
||||
|
||||
public AuthenticationResult(boolean authenticated, AuthenticatedUser user) {
|
||||
|
||||
this.authenticated = authenticated;
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public static AuthenticationResult notAuthenticated() {
|
||||
return new AuthenticationResult(false, null);
|
||||
}
|
||||
|
||||
public boolean isAuthenticated() {
|
||||
return authenticated;
|
||||
}
|
||||
|
||||
public AuthenticatedUser getUser() {
|
||||
return user;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.core;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Prints out all runtime arguments passed in as JVM arguments (<i>-D</i>).
|
||||
* </p>
|
||||
*
|
||||
* @author Josh Stark
|
||||
*/
|
||||
abstract class BaseRuntimeLoader {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(BaseRuntimeLoader.class);
|
||||
|
||||
BaseRuntimeLoader() {
|
||||
|
||||
LOGGER.info("Initalising...");
|
||||
LOGGER.info("Config base : " + FleetRuntime.CONFIG_BASE);
|
||||
LOGGER.info("Show Passwords : " + FleetRuntime.SHOW_PASSWORDS);
|
||||
}
|
||||
}
|
||||
74
src/main/java/io/linuxserver/fleet/core/FleetApp.java
Normal file
74
src/main/java/io/linuxserver/fleet/core/FleetApp.java
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.core;
|
||||
|
||||
import io.linuxserver.fleet.thread.SynchroniseAllRepositoriesTask;
|
||||
import io.linuxserver.fleet.web.pages.HomePage;
|
||||
import io.linuxserver.fleet.web.pages.LoginPage;
|
||||
import io.linuxserver.fleet.web.pages.ManageRepositoriesPage;
|
||||
import io.linuxserver.fleet.web.routes.*;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Primary entry point for the application. All contexts and resources are loaded
|
||||
* through this class.
|
||||
* </p>
|
||||
*/
|
||||
class FleetApp {
|
||||
|
||||
private final FleetBeans beans;
|
||||
|
||||
FleetApp() {
|
||||
beans = new FleetBeans();
|
||||
}
|
||||
|
||||
void run() {
|
||||
|
||||
migrateDatabase();
|
||||
configureWeb();
|
||||
scheduleSync();
|
||||
|
||||
// beans.getSynchronisationDelegate().synchronise();
|
||||
}
|
||||
|
||||
private void migrateDatabase() {
|
||||
beans.getDatabaseVersion().migrate();
|
||||
}
|
||||
|
||||
private void configureWeb() {
|
||||
|
||||
beans.getWebServer().start();
|
||||
beans.getWebServer().addPage("/", new HomePage(beans.getRepositoryDelegate(), beans.getImageDelegate()));
|
||||
beans.getWebServer().addPage("/admin", new ManageRepositoriesPage(beans.getRepositoryDelegate()));
|
||||
beans.getWebServer().addPage("/admin/login", new LoginPage());
|
||||
beans.getWebServer().addPostRoute("/admin/login", new LoginRoute(beans.getAuthenticationDelegate()));
|
||||
beans.getWebServer().addPostRoute("/admin/logout", new LogoutRoute());
|
||||
beans.getWebServer().addGetApi("/api/v1/images", new AllImagesApi(beans.getRepositoryDelegate(), beans.getImageDelegate()));
|
||||
beans.getWebServer().addPostApi("/admin/manageImage", new ManageImageApi(beans.getImageDelegate()));
|
||||
beans.getWebServer().addPostApi("/admin/manageRepository", new ManageRepositoryApi(beans.getRepositoryDelegate()));
|
||||
}
|
||||
|
||||
private void scheduleSync() {
|
||||
|
||||
beans.getTaskManager().scheduleRecurringTask(
|
||||
new SynchroniseAllRepositoriesTask(beans.getSynchronisationDelegate()), beans.getProperties().getRefreshIntervalInMinutes(), TimeUnit.MINUTES
|
||||
);
|
||||
}
|
||||
}
|
||||
117
src/main/java/io/linuxserver/fleet/core/FleetBeans.java
Normal file
117
src/main/java/io/linuxserver/fleet/core/FleetBeans.java
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.core;
|
||||
|
||||
import io.linuxserver.fleet.db.DefaultDatabaseConnection;
|
||||
import io.linuxserver.fleet.db.dao.DefaultImageDAO;
|
||||
import io.linuxserver.fleet.db.dao.DefaultRepositoryDAO;
|
||||
import io.linuxserver.fleet.db.migration.DatabaseVersion;
|
||||
import io.linuxserver.fleet.delegate.*;
|
||||
import io.linuxserver.fleet.dockerhub.DockerHubV2Client;
|
||||
import io.linuxserver.fleet.thread.TaskManager;
|
||||
import io.linuxserver.fleet.web.WebServer;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Initialises all relevant dependencies for the Fleet application
|
||||
* </p>
|
||||
*/
|
||||
public class FleetBeans {
|
||||
|
||||
/**
|
||||
* Contains all runtime properties that get loaded into the application on start-up
|
||||
*/
|
||||
private final FleetProperties properties;
|
||||
|
||||
/**
|
||||
* Facade layer which handles delegation for image management.
|
||||
*/
|
||||
private final ImageDelegate imageDelegate;
|
||||
|
||||
private final RepositoryDelegate repositoryDelegate;
|
||||
|
||||
private final AuthenticationDelegate authenticationDelegate;
|
||||
|
||||
/**
|
||||
* Facade layer for interaction with the Docker Hub APIs.
|
||||
*/
|
||||
private final DockerHubDelegate dockerHubDelegate;
|
||||
|
||||
private final SynchronisationDelegate synchronisationDelegate;
|
||||
|
||||
private final WebServer webServer;
|
||||
|
||||
private final TaskManager taskManager;
|
||||
|
||||
/**
|
||||
* Ensures the database is kept up to date.
|
||||
*/
|
||||
private final DatabaseVersion databaseVersion;
|
||||
|
||||
FleetBeans() {
|
||||
|
||||
properties = new PropertiesLoader().getProperties();
|
||||
|
||||
final DefaultDatabaseConnection databaseConnection = new DefaultDatabaseConnection(properties);
|
||||
|
||||
databaseVersion = new DatabaseVersion(databaseConnection);
|
||||
imageDelegate = new ImageDelegate(new DefaultImageDAO(databaseConnection));
|
||||
repositoryDelegate = new RepositoryDelegate(new DefaultRepositoryDAO(databaseConnection));
|
||||
dockerHubDelegate = new DockerHubDelegate(new DockerHubV2Client(properties.getDockerHubCredentials()));
|
||||
authenticationDelegate = new PropertiesAuthenticationDelegate(properties.getAppUsername(), properties.getAppPassword());
|
||||
synchronisationDelegate = new SynchronisationDelegate(imageDelegate, repositoryDelegate, dockerHubDelegate);
|
||||
webServer = new WebServer(properties.getAppPort());
|
||||
taskManager = new TaskManager();
|
||||
}
|
||||
|
||||
public FleetProperties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public ImageDelegate getImageDelegate() {
|
||||
return imageDelegate;
|
||||
}
|
||||
|
||||
public DockerHubDelegate getDockerHubDelegate() {
|
||||
return dockerHubDelegate;
|
||||
}
|
||||
|
||||
public RepositoryDelegate getRepositoryDelegate() {
|
||||
return repositoryDelegate;
|
||||
}
|
||||
|
||||
public SynchronisationDelegate getSynchronisationDelegate() {
|
||||
return synchronisationDelegate;
|
||||
}
|
||||
|
||||
public AuthenticationDelegate getAuthenticationDelegate() {
|
||||
return authenticationDelegate;
|
||||
}
|
||||
|
||||
public DatabaseVersion getDatabaseVersion() {
|
||||
return databaseVersion;
|
||||
}
|
||||
|
||||
public WebServer getWebServer() {
|
||||
return webServer;
|
||||
}
|
||||
|
||||
public TaskManager getTaskManager() {
|
||||
return taskManager;
|
||||
}
|
||||
}
|
||||
71
src/main/java/io/linuxserver/fleet/core/FleetProperties.java
Normal file
71
src/main/java/io/linuxserver/fleet/core/FleetProperties.java
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.core;
|
||||
|
||||
import io.linuxserver.fleet.dockerhub.DockerHubCredentials;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
public class FleetProperties {
|
||||
|
||||
private Properties properties;
|
||||
|
||||
FleetProperties(Properties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public String getDatabaseDriverClassName() {
|
||||
return properties.getProperty("fleet.database.driver");
|
||||
}
|
||||
|
||||
public String getDatabaseUrl() {
|
||||
return properties.getProperty("fleet.database.url");
|
||||
}
|
||||
|
||||
public String getDatabaseUsername() {
|
||||
return properties.getProperty("fleet.database.username");
|
||||
}
|
||||
|
||||
public String getDatabasePassword() {
|
||||
return properties.getProperty("fleet.database.password");
|
||||
}
|
||||
|
||||
public String getAppUsername() {
|
||||
return properties.getProperty("fleet.admin.username");
|
||||
}
|
||||
|
||||
public String getAppPassword() {
|
||||
return properties.getProperty("fleet.admin.password");
|
||||
}
|
||||
|
||||
public int getAppPort() {
|
||||
return Integer.parseInt(properties.getProperty("fleet.app.port"));
|
||||
}
|
||||
|
||||
public int getRefreshIntervalInMinutes() {
|
||||
return Integer.parseInt(properties.getProperty("fleet.refresh.interval"));
|
||||
}
|
||||
|
||||
public DockerHubCredentials getDockerHubCredentials() {
|
||||
|
||||
String username = properties.getProperty("fleet.dockerhub.username");
|
||||
String password = properties.getProperty("fleet.dockerhub.password");
|
||||
|
||||
return new DockerHubCredentials(username, password);
|
||||
}
|
||||
}
|
||||
31
src/main/java/io/linuxserver/fleet/core/FleetRuntime.java
Normal file
31
src/main/java/io/linuxserver/fleet/core/FleetRuntime.java
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.core;
|
||||
|
||||
public interface FleetRuntime {
|
||||
|
||||
/**
|
||||
* Base directory for the config file.
|
||||
*/
|
||||
String CONFIG_BASE = System.getProperty("fleet.config.base");
|
||||
|
||||
/**
|
||||
* Whether or not logs should show passwords
|
||||
*/
|
||||
boolean SHOW_PASSWORDS = System.getProperty("fleet.show.passwords") != null;
|
||||
}
|
||||
27
src/main/java/io/linuxserver/fleet/core/Main.java
Normal file
27
src/main/java/io/linuxserver/fleet/core/Main.java
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.core;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
FleetApp app = new FleetApp();
|
||||
app.run();
|
||||
}
|
||||
}
|
||||
117
src/main/java/io/linuxserver/fleet/core/PropertiesLoader.java
Normal file
117
src/main/java/io/linuxserver/fleet/core/PropertiesLoader.java
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.core;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Loads in the application properties from disk. Properties must be provided as JVM arguments
|
||||
* as these will be used to create a connection to the underlying database, and any other
|
||||
* required connections.
|
||||
* </p>
|
||||
*
|
||||
* @author Josh Stark
|
||||
*/
|
||||
public class PropertiesLoader extends BaseRuntimeLoader {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(PropertiesLoader.class);
|
||||
|
||||
private final FleetProperties properties;
|
||||
|
||||
PropertiesLoader() {
|
||||
|
||||
super();
|
||||
|
||||
try {
|
||||
|
||||
Properties properties = new Properties();
|
||||
properties.load(new FileInputStream(FleetRuntime.CONFIG_BASE + "/fleet.properties"));
|
||||
|
||||
this.properties = new FleetProperties(properties);
|
||||
|
||||
printProperties();
|
||||
|
||||
if (!createStaticFileDirectory()) {
|
||||
throw new RuntimeException("Unable to create the static files location for Fleet. Check permissions");
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
|
||||
LOGGER.error("Unable to load config! Check JVM args and config directory", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean createStaticFileDirectory() {
|
||||
|
||||
File staticFilesDir = new File(FleetRuntime.CONFIG_BASE + "/fleet_static");
|
||||
|
||||
if (staticFilesDir.exists()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return staticFilesDir.mkdir();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The loaded properties, with accessible fields.
|
||||
* </p>
|
||||
*
|
||||
* @return
|
||||
* All application properties.
|
||||
*/
|
||||
protected FleetProperties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Prints out the loaded properties to the log. Useful when the application loads up for the first time.
|
||||
* </p>
|
||||
*/
|
||||
private void printProperties() {
|
||||
|
||||
LOGGER.info("fleet.app.port : " + properties.getAppPort());
|
||||
LOGGER.info("fleet.refresh.interval : " + properties.getRefreshIntervalInMinutes());
|
||||
LOGGER.info("fleet.database.url : " + properties.getDatabaseUrl());
|
||||
LOGGER.info("fleet.database.username : " + properties.getDatabaseUsername());
|
||||
LOGGER.info("fleet.database.password : " + (showPasswords() ? properties.getDatabasePassword() : "***"));
|
||||
LOGGER.info("fleet.dockerhub.username : " + properties.getDockerHubCredentials().getUsername());
|
||||
LOGGER.info("fleet.dockerhub.password : " + (showPasswords() ? properties.getDockerHubCredentials().getPassword() : "***"));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Ideally in a production environment you don't want to log passwords or keys.
|
||||
* </p>
|
||||
*
|
||||
* @return
|
||||
* true if passwords can be logged to the terminal.
|
||||
*/
|
||||
private boolean showPasswords() {
|
||||
return FleetRuntime.SHOW_PASSWORDS;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.db;
|
||||
|
||||
import io.linuxserver.fleet.core.FleetProperties;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class DefaultDatabaseConnection extends PoolingDatabaseConnection {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultDatabaseConnection.class);
|
||||
|
||||
public DefaultDatabaseConnection(FleetProperties properties) {
|
||||
super(properties);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.db;
|
||||
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import io.linuxserver.fleet.core.FleetProperties;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public abstract class PoolingDatabaseConnection {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(PoolingDatabaseConnection.class);
|
||||
|
||||
private final HikariDataSource dataSource;
|
||||
|
||||
PoolingDatabaseConnection(FleetProperties properties) {
|
||||
|
||||
dataSource = new HikariDataSource();
|
||||
dataSource.setDriverClassName(properties.getDatabaseDriverClassName());
|
||||
dataSource.setJdbcUrl(properties.getDatabaseUrl());
|
||||
dataSource.setUsername(properties.getDatabaseUsername());
|
||||
dataSource.setPassword(properties.getDatabasePassword());
|
||||
|
||||
LOGGER.info("DataSource established: " + dataSource.getJdbcUrl());
|
||||
}
|
||||
|
||||
public DataSource getDataSource() {
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
public Connection getConnection() throws SQLException {
|
||||
return dataSource.getConnection();
|
||||
}
|
||||
}
|
||||
178
src/main/java/io/linuxserver/fleet/db/dao/DefaultImageDAO.java
Normal file
178
src/main/java/io/linuxserver/fleet/db/dao/DefaultImageDAO.java
Normal file
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.db.dao;
|
||||
|
||||
import io.linuxserver.fleet.db.PoolingDatabaseConnection;
|
||||
import io.linuxserver.fleet.db.query.InsertUpdateResult;
|
||||
import io.linuxserver.fleet.db.query.InsertUpdateStatus;
|
||||
import io.linuxserver.fleet.db.query.LimitedResult;
|
||||
import io.linuxserver.fleet.model.Image;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static io.linuxserver.fleet.db.dao.Utils.setNullableInt;
|
||||
import static io.linuxserver.fleet.db.dao.Utils.setNullableLong;
|
||||
|
||||
public class DefaultImageDAO implements ImageDAO {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultImageDAO.class);
|
||||
|
||||
private final PoolingDatabaseConnection databaseConnection;
|
||||
|
||||
public DefaultImageDAO(PoolingDatabaseConnection databaseConnection) {
|
||||
this.databaseConnection = databaseConnection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Image findImageByRepositoryAndImageName(int repositoryId, String imageName) {
|
||||
|
||||
try (Connection connection = databaseConnection.getConnection()) {
|
||||
|
||||
CallableStatement call = connection.prepareCall("{CALL Image_GetByName(?,?)}");
|
||||
call.setInt(1, repositoryId);
|
||||
call.setString(2, imageName);
|
||||
|
||||
ResultSet results = call.executeQuery();
|
||||
if (results.next())
|
||||
return parseImageFromResultSet(results);
|
||||
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Unable to fetch image", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Image fetchImage(Integer id) {
|
||||
|
||||
LOGGER.debug("Fetching image by ID: " + id);
|
||||
|
||||
try (Connection connection = databaseConnection.getConnection()) {
|
||||
|
||||
CallableStatement call = connection.prepareCall("{CALL Image_Get(?)}");
|
||||
call.setInt(1, id);
|
||||
|
||||
ResultSet results = call.executeQuery();
|
||||
if (results.next())
|
||||
return parseImageFromResultSet(results);
|
||||
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Unable to fetch image.", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LimitedResult<Image> fetchImagesByRepository(int repositoryId) {
|
||||
|
||||
List<Image> images = new ArrayList<>();
|
||||
|
||||
try (Connection connection = databaseConnection.getConnection()) {
|
||||
|
||||
CallableStatement call = connection.prepareCall("{CALL Image_GetAll(?,?)}");
|
||||
call.setInt(1, repositoryId);
|
||||
call.registerOutParameter(2, Types.INTEGER);
|
||||
|
||||
ResultSet results = call.executeQuery();
|
||||
while (results.next())
|
||||
images.add(parseImageFromResultSet(results));
|
||||
|
||||
int totalRecords = call.getInt(2);
|
||||
return new LimitedResult<>(images, totalRecords);
|
||||
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Unable to get all images", e);
|
||||
}
|
||||
|
||||
return new LimitedResult<>(images, images.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public InsertUpdateResult<Image> saveImage(Image image) {
|
||||
|
||||
try (Connection connection = databaseConnection.getConnection()) {
|
||||
|
||||
CallableStatement call = connection.prepareCall("{CALL Image_Save(?,?,?,?,?,?,?,?,?,?)");
|
||||
setNullableInt(call, 1, image.getId());
|
||||
call.setInt(2, image.getRepositoryId());
|
||||
call.setString(3, image.getName());
|
||||
setNullableLong(call, 4, image.getPullCount());
|
||||
call.setString(5, image.getVersion());
|
||||
call.setBoolean(6, image.isHidden());
|
||||
call.setBoolean(7, image.isUnstable());
|
||||
|
||||
call.registerOutParameter(8, Types.INTEGER);
|
||||
call.registerOutParameter(9, Types.INTEGER);
|
||||
call.registerOutParameter(10, Types.VARCHAR);
|
||||
|
||||
call.executeUpdate();
|
||||
|
||||
int imageId = call.getInt(8);
|
||||
int status = call.getInt(9);
|
||||
String statusMessage = call.getString(10);
|
||||
|
||||
if (InsertUpdateStatus.OK == status)
|
||||
return new InsertUpdateResult<>(fetchImage(imageId), status, statusMessage);
|
||||
|
||||
return new InsertUpdateResult<>(status, statusMessage);
|
||||
|
||||
} catch (SQLException e) {
|
||||
|
||||
LOGGER.error("Unable to save image", e);
|
||||
return new InsertUpdateResult<>(null, InsertUpdateStatus.OK, "Unable to save image");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeImage(Integer id) {
|
||||
|
||||
try (Connection connection = databaseConnection.getConnection()) {
|
||||
|
||||
PreparedStatement call = connection.prepareStatement("DELETE FROM Images WHERE `id` = ?");
|
||||
call.setInt(1, id);
|
||||
|
||||
call.executeUpdate();
|
||||
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Error when removing image", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Image parseImageFromResultSet(ResultSet results) throws SQLException {
|
||||
|
||||
Image image = new Image(
|
||||
results.getInt("ImageId"),
|
||||
results.getInt("RepositoryId"),
|
||||
results.getString("ImageName")
|
||||
);
|
||||
|
||||
return image
|
||||
.withVersion(results.getString("ImageVersion"))
|
||||
.withPullCount(results.getLong("ImagePullCount"))
|
||||
.withVersionMask(results.getString("ImageVersionMask"))
|
||||
.withModifiedTime(results.getTimestamp("ModifiedTime").toLocalDateTime())
|
||||
.withHidden(results.getBoolean("ImageHidden"))
|
||||
.withUnstable(results.getBoolean("ImageUnstable"));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.db.dao;
|
||||
|
||||
import io.linuxserver.fleet.db.PoolingDatabaseConnection;
|
||||
import io.linuxserver.fleet.db.query.InsertUpdateResult;
|
||||
import io.linuxserver.fleet.db.query.InsertUpdateStatus;
|
||||
import io.linuxserver.fleet.model.Repository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static io.linuxserver.fleet.db.dao.Utils.setNullableInt;
|
||||
import static io.linuxserver.fleet.db.dao.Utils.setNullableString;
|
||||
|
||||
public class DefaultRepositoryDAO implements RepositoryDAO {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultImageDAO.class);
|
||||
|
||||
private final PoolingDatabaseConnection databaseConnection;
|
||||
|
||||
public DefaultRepositoryDAO(PoolingDatabaseConnection databaseConnection) {
|
||||
this.databaseConnection = databaseConnection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Repository fetchRepository(int id) {
|
||||
|
||||
try (Connection connection = databaseConnection.getConnection()) {
|
||||
|
||||
CallableStatement call = connection.prepareCall("{CALL Repository_Get(?)}");
|
||||
call.setInt(1, id);
|
||||
|
||||
ResultSet results = call.executeQuery();
|
||||
if (results.next())
|
||||
return parseRepositoryFromResultSet(results);
|
||||
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Unable to retrieve repository", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InsertUpdateResult<Repository> saveRepository(Repository repository) {
|
||||
|
||||
try (Connection connection = databaseConnection.getConnection()) {
|
||||
|
||||
CallableStatement call = connection.prepareCall("{CALL Repository_Save(?,?,?,?,?,?,?)}");
|
||||
setNullableInt(call, 1, repository.getId());
|
||||
call.setString(2, repository.getName());
|
||||
setNullableString(call, 3, repository.getVersionMask());
|
||||
call.setBoolean(4, repository.isSyncEnabled());
|
||||
|
||||
call.registerOutParameter(5, Types.INTEGER);
|
||||
call.registerOutParameter(6, Types.INTEGER);
|
||||
call.registerOutParameter(7, Types.VARCHAR);
|
||||
|
||||
call.executeUpdate();
|
||||
|
||||
int repositoryId = call.getInt(5);
|
||||
int status = call.getInt(6);
|
||||
String statusMessage = call.getString(7);
|
||||
|
||||
if (InsertUpdateStatus.OK == status)
|
||||
return new InsertUpdateResult<>(fetchRepository(repositoryId), status, statusMessage);
|
||||
|
||||
return new InsertUpdateResult<>(status, statusMessage);
|
||||
|
||||
} catch (SQLException e) {
|
||||
|
||||
LOGGER.error("Unable to save repository", e);
|
||||
return new InsertUpdateResult<>(null, InsertUpdateStatus.OK, "Unable to save repository");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Repository> fetchAllRepositories() {
|
||||
|
||||
List<Repository> repositories = new ArrayList<>();
|
||||
|
||||
try (Connection connection = databaseConnection.getConnection()) {
|
||||
|
||||
CallableStatement call = connection.prepareCall("{CALL Repository_GetAll()}");
|
||||
|
||||
ResultSet results = call.executeQuery();
|
||||
while (results.next())
|
||||
repositories.add(parseRepositoryFromResultSet(results));
|
||||
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Unable to get all repositories", e);
|
||||
}
|
||||
|
||||
return repositories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Repository findRepositoryByName(String name) {
|
||||
|
||||
try (Connection connection = databaseConnection.getConnection()) {
|
||||
|
||||
CallableStatement call = connection.prepareCall("{CALL Repository_GetByName(?)}");
|
||||
call.setString(1, name);
|
||||
|
||||
ResultSet results = call.executeQuery();
|
||||
if (results.next())
|
||||
return parseRepositoryFromResultSet(results);
|
||||
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Unable to retrieve repository", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRepository(int id) {
|
||||
|
||||
try (Connection connection = databaseConnection.getConnection()) {
|
||||
|
||||
CallableStatement call = connection.prepareCall("{CALL Repository_Delete(?)}");
|
||||
call.setInt(1, id);
|
||||
|
||||
call.executeUpdate();
|
||||
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Error when removing repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Repository parseRepositoryFromResultSet(ResultSet results) throws SQLException {
|
||||
|
||||
Repository repository = new Repository(results.getInt("RepositoryId"), results.getString("RepositoryName"))
|
||||
.withSyncEnabled(results.getBoolean("SyncEnabled"))
|
||||
.withVersionMask(results.getString("RepositoryVersionMask"))
|
||||
.withModifiedTime(results.getTimestamp("ModifiedTime").toLocalDateTime());
|
||||
|
||||
LOGGER.debug("Parsed repository: " + repository);
|
||||
return repository;
|
||||
}
|
||||
}
|
||||
18
src/main/java/io/linuxserver/fleet/db/dao/ImageDAO.java
Normal file
18
src/main/java/io/linuxserver/fleet/db/dao/ImageDAO.java
Normal file
@ -0,0 +1,18 @@
|
||||
package io.linuxserver.fleet.db.dao;
|
||||
|
||||
import io.linuxserver.fleet.db.query.InsertUpdateResult;
|
||||
import io.linuxserver.fleet.db.query.LimitedResult;
|
||||
import io.linuxserver.fleet.model.Image;
|
||||
|
||||
public interface ImageDAO {
|
||||
|
||||
Image findImageByRepositoryAndImageName(int repositoryId, String imageName);
|
||||
|
||||
Image fetchImage(Integer id);
|
||||
|
||||
LimitedResult<Image> fetchImagesByRepository(int repositoryId);
|
||||
|
||||
InsertUpdateResult<Image> saveImage(Image image);
|
||||
|
||||
void removeImage(Integer id);
|
||||
}
|
||||
19
src/main/java/io/linuxserver/fleet/db/dao/RepositoryDAO.java
Normal file
19
src/main/java/io/linuxserver/fleet/db/dao/RepositoryDAO.java
Normal file
@ -0,0 +1,19 @@
|
||||
package io.linuxserver.fleet.db.dao;
|
||||
|
||||
import io.linuxserver.fleet.db.query.InsertUpdateResult;
|
||||
import io.linuxserver.fleet.model.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface RepositoryDAO {
|
||||
|
||||
Repository fetchRepository(int id);
|
||||
|
||||
InsertUpdateResult<Repository> saveRepository(Repository repository);
|
||||
|
||||
List<Repository> fetchAllRepositories();
|
||||
|
||||
Repository findRepositoryByName(String name);
|
||||
|
||||
void removeRepository(int id);
|
||||
}
|
||||
52
src/main/java/io/linuxserver/fleet/db/dao/Utils.java
Normal file
52
src/main/java/io/linuxserver/fleet/db/dao/Utils.java
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.db.dao;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
|
||||
class Utils {
|
||||
|
||||
static void setNullableInt(CallableStatement call, int position, Integer value) throws SQLException {
|
||||
|
||||
if (null == value)
|
||||
call.setNull(position, Types.INTEGER);
|
||||
|
||||
else
|
||||
call.setInt(position, value);
|
||||
}
|
||||
|
||||
static void setNullableLong(CallableStatement call, int position, Long value) throws SQLException {
|
||||
|
||||
if (null == value)
|
||||
call.setNull(position, Types.BIGINT);
|
||||
|
||||
else
|
||||
call.setLong(position, value);
|
||||
}
|
||||
|
||||
static void setNullableString(CallableStatement call, int position, String value) throws SQLException {
|
||||
|
||||
if (null == value)
|
||||
call.setNull(position, Types.VARCHAR);
|
||||
|
||||
else
|
||||
call.setString(position, value);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.db.migration;
|
||||
|
||||
import io.linuxserver.fleet.db.PoolingDatabaseConnection;
|
||||
import org.flywaydb.core.Flyway;
|
||||
import org.flywaydb.core.api.FlywayException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Manages versioning of the database to ensure it is kept up-to-date
|
||||
* </p>
|
||||
*/
|
||||
public class DatabaseVersion {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseVersion.class);
|
||||
|
||||
private final Flyway flyway;
|
||||
|
||||
public DatabaseVersion(PoolingDatabaseConnection databaseConnection) {
|
||||
flyway = Flyway.configure().dataSource(databaseConnection.getDataSource()).load();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Runs the migration process which runs any necessary scripts to get the database configured.
|
||||
* </p>
|
||||
*/
|
||||
public void migrate() {
|
||||
|
||||
try {
|
||||
|
||||
flyway.migrate();
|
||||
|
||||
} catch (FlywayException e) {
|
||||
|
||||
LOGGER.error(e.getMessage());
|
||||
throw new RuntimeException("Unable to start application because the database has gone out of sync.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.db.query;
|
||||
|
||||
public class InsertUpdateResult<T> {
|
||||
|
||||
private final T result;
|
||||
private final int status;
|
||||
private final String statusMessage;
|
||||
|
||||
public InsertUpdateResult(T result, int status, String statusMessage) {
|
||||
|
||||
this.result = result;
|
||||
this.status = status;
|
||||
this.statusMessage = statusMessage;
|
||||
}
|
||||
|
||||
public InsertUpdateResult(int status, String statusMessage) {
|
||||
this(null, status, statusMessage);
|
||||
}
|
||||
|
||||
public T getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public int getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public String getStatusMessage() {
|
||||
return statusMessage;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.db.query;
|
||||
|
||||
public interface InsertUpdateStatus {
|
||||
|
||||
int OK = 0;
|
||||
int FAILED = 1;
|
||||
}
|
||||
38
src/main/java/io/linuxserver/fleet/db/query/LimitOffset.java
Normal file
38
src/main/java/io/linuxserver/fleet/db/query/LimitOffset.java
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.db.query;
|
||||
|
||||
public class LimitOffset {
|
||||
|
||||
private final int limit;
|
||||
private final int offset;
|
||||
|
||||
public LimitOffset(int limit, int offset) {
|
||||
|
||||
this.limit = limit;
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
public int getLimit() {
|
||||
return limit;
|
||||
}
|
||||
|
||||
public int getOffset() {
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.db.query;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class LimitedResult<T> {
|
||||
|
||||
private final List<T> results;
|
||||
private final LimitOffset next;
|
||||
private final int totalCount;
|
||||
|
||||
public LimitedResult(List<T> results, int totalCount) {
|
||||
this(results, totalCount,null);
|
||||
}
|
||||
|
||||
public LimitedResult(List<T> results, int totalCount, LimitOffset next) {
|
||||
|
||||
this.results = results;
|
||||
this.totalCount = totalCount;
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
public List<T> getResults() {
|
||||
return results;
|
||||
}
|
||||
|
||||
public int getTotalCount() {
|
||||
return totalCount;
|
||||
}
|
||||
|
||||
public LimitOffset getNext() {
|
||||
return next;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.delegate;
|
||||
|
||||
import io.linuxserver.fleet.auth.AuthenticationResult;
|
||||
|
||||
public interface AuthenticationDelegate {
|
||||
|
||||
AuthenticationResult authenticate(String username, String password);
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.delegate;
|
||||
|
||||
import io.linuxserver.fleet.dockerhub.DockerHubClient;
|
||||
import io.linuxserver.fleet.dockerhub.model.DockerHubV2Image;
|
||||
import io.linuxserver.fleet.model.DockerHubImage;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class DockerHubDelegate {
|
||||
|
||||
private final DockerHubClient dockerHubClient;
|
||||
|
||||
public DockerHubDelegate(DockerHubClient dockerHubClient) {
|
||||
this.dockerHubClient = dockerHubClient;
|
||||
}
|
||||
|
||||
public List<String> fetchAllRepositories() {
|
||||
return dockerHubClient.fetchAllRepositories().getNamespaces();
|
||||
}
|
||||
|
||||
public List<DockerHubImage> fetchAllImagesFromRepository(String repositoryName) {
|
||||
|
||||
List<DockerHubImage> images = new ArrayList<>();
|
||||
|
||||
for (DockerHubV2Image apiImage : dockerHubClient.fetchImagesFromRepository(repositoryName))
|
||||
images.add(convertApiImageToInternalImage(apiImage));
|
||||
|
||||
images.sort(Comparator.comparing(DockerHubImage::getName));
|
||||
|
||||
return images;
|
||||
}
|
||||
|
||||
public String fetchLatestImageTag(String repositoryName, String imageName) {
|
||||
return dockerHubClient.fetchLatestTagForImage(repositoryName, imageName).getName();
|
||||
}
|
||||
|
||||
private DockerHubImage convertApiImageToInternalImage(DockerHubV2Image apiImage) {
|
||||
|
||||
return new DockerHubImage(
|
||||
apiImage.getName(),
|
||||
apiImage.getNamespace(),
|
||||
apiImage.getDescription(),
|
||||
apiImage.getStarCount(),
|
||||
apiImage.getPullCount(),
|
||||
parseDockerHubDate(apiImage.getLastUpdated())
|
||||
);
|
||||
}
|
||||
|
||||
private LocalDateTime parseDockerHubDate(String date) {
|
||||
|
||||
if (null == date)
|
||||
return null;
|
||||
|
||||
return LocalDateTime.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'"));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.delegate;
|
||||
|
||||
import io.linuxserver.fleet.db.dao.ImageDAO;
|
||||
import io.linuxserver.fleet.db.query.InsertUpdateResult;
|
||||
import io.linuxserver.fleet.db.query.InsertUpdateStatus;
|
||||
import io.linuxserver.fleet.exception.SaveException;
|
||||
import io.linuxserver.fleet.model.Image;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ImageDelegate {
|
||||
|
||||
private final ImageDAO imageDAO;
|
||||
|
||||
public ImageDelegate(ImageDAO imageDAO) {
|
||||
this.imageDAO = imageDAO;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Performs a look-up to see if an image exists in the persisted database which matches
|
||||
* the given repository and image name. This is used to determine if, when scanning, Fleet
|
||||
* already has the image stored against the repository.
|
||||
* </p>
|
||||
*/
|
||||
public Image findImageByRepositoryAndImageName(int repositoryId, String imageName) {
|
||||
return imageDAO.findImageByRepositoryAndImageName(repositoryId, imageName);
|
||||
}
|
||||
|
||||
public Image fetchImage(int id) {
|
||||
return imageDAO.fetchImage(id);
|
||||
}
|
||||
|
||||
public List<Image> fetchImagesByRepository(int repositoryId) {
|
||||
return imageDAO.fetchImagesByRepository(repositoryId).getResults();
|
||||
}
|
||||
|
||||
public void removeImage(Integer id) {
|
||||
imageDAO.removeImage(id);
|
||||
}
|
||||
|
||||
public Image saveImage(Image image) throws SaveException {
|
||||
|
||||
InsertUpdateResult<Image> result = imageDAO.saveImage(image);
|
||||
|
||||
if (result.getStatus() == InsertUpdateStatus.OK)
|
||||
return result.getResult();
|
||||
|
||||
throw new SaveException(result.getStatusMessage());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.delegate;
|
||||
|
||||
import io.linuxserver.fleet.auth.AuthenticatedUser;
|
||||
import io.linuxserver.fleet.auth.AuthenticationResult;
|
||||
|
||||
public class PropertiesAuthenticationDelegate implements AuthenticationDelegate {
|
||||
|
||||
private final String adminUsername;
|
||||
private final String adminPassword;
|
||||
|
||||
public PropertiesAuthenticationDelegate(String username, String password) {
|
||||
|
||||
this.adminUsername = username;
|
||||
this.adminPassword = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticationResult authenticate(String username, String password) {
|
||||
|
||||
if (adminUsername.equals(username) && adminPassword.equals(password))
|
||||
return new AuthenticationResult(true, new AuthenticatedUser(username));
|
||||
|
||||
return AuthenticationResult.notAuthenticated();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.delegate;
|
||||
|
||||
import io.linuxserver.fleet.db.dao.RepositoryDAO;
|
||||
import io.linuxserver.fleet.db.query.InsertUpdateResult;
|
||||
import io.linuxserver.fleet.db.query.InsertUpdateStatus;
|
||||
import io.linuxserver.fleet.exception.SaveException;
|
||||
import io.linuxserver.fleet.model.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class RepositoryDelegate {
|
||||
|
||||
private final RepositoryDAO repositoryDAO;
|
||||
|
||||
public RepositoryDelegate(RepositoryDAO repositoryDAO) {
|
||||
this.repositoryDAO = repositoryDAO;
|
||||
}
|
||||
|
||||
public Repository fetchRepository(int id) {
|
||||
return repositoryDAO.fetchRepository(id);
|
||||
}
|
||||
|
||||
public Repository saveRepository(Repository repository) throws SaveException {
|
||||
|
||||
InsertUpdateResult<Repository> result = repositoryDAO.saveRepository(repository);
|
||||
|
||||
if (result.getStatus() == InsertUpdateStatus.OK)
|
||||
return result.getResult();
|
||||
|
||||
throw new SaveException(result.getStatusMessage());
|
||||
}
|
||||
|
||||
public List<Repository> fetchAllRepositories() {
|
||||
return repositoryDAO.fetchAllRepositories();
|
||||
}
|
||||
|
||||
public Repository findRepositoryByName(String name) {
|
||||
return repositoryDAO.findRepositoryByName(name);
|
||||
}
|
||||
|
||||
public void removeRepository(int id) {
|
||||
repositoryDAO.removeRepository(id);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.delegate;
|
||||
|
||||
import io.linuxserver.fleet.exception.SaveException;
|
||||
import io.linuxserver.fleet.model.DockerHubImage;
|
||||
import io.linuxserver.fleet.model.Image;
|
||||
import io.linuxserver.fleet.model.Repository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Handles the one-way synchronisation of Docker Hub repositories and images over to the Fleet
|
||||
* database. Any newly created image in Docker Hub will be automatically picked up and stored, but any
|
||||
* new repositories will be marked as skipped until someone manually sets it to be synchronised.
|
||||
* </p>
|
||||
*/
|
||||
public class SynchronisationDelegate {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(SynchronisationDelegate.class);
|
||||
|
||||
private final ImageDelegate imageDelegate;
|
||||
private final RepositoryDelegate repositoryDelegate;
|
||||
private final DockerHubDelegate dockerHubDelegate;
|
||||
|
||||
public SynchronisationDelegate(ImageDelegate imageDelegate, RepositoryDelegate repositoryDelegate, DockerHubDelegate dockerHubDelegate) {
|
||||
|
||||
this.imageDelegate = imageDelegate;
|
||||
this.repositoryDelegate = repositoryDelegate;
|
||||
this.dockerHubDelegate = dockerHubDelegate;
|
||||
}
|
||||
|
||||
public void synchronise() {
|
||||
synchronise((message) -> LOGGER.info(message.toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Scans Docker Hub for all images against all registered repositories for the user. It will then update the pull
|
||||
* and version information for each.
|
||||
* </p>
|
||||
*/
|
||||
public void synchronise(SynchronisationListener listener) {
|
||||
|
||||
if (null == listener) {
|
||||
|
||||
synchronise();
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.info("Starting synchronisation of all repositories");
|
||||
|
||||
List<String> repositories = dockerHubDelegate.fetchAllRepositories();
|
||||
|
||||
for (String repositoryName : repositories)
|
||||
synchroniseRepository(repositoryName, listener);
|
||||
|
||||
LOGGER.info("Synchronisation complete");
|
||||
}
|
||||
|
||||
private void synchroniseRepository(String repositoryName, SynchronisationListener listener) {
|
||||
|
||||
Repository repository = configureRepository(repositoryName);
|
||||
if (repository.isSyncEnabled()) {
|
||||
|
||||
listener.onEvent("Synchronising " + repository);
|
||||
List<DockerHubImage> images = dockerHubDelegate.fetchAllImagesFromRepository(repository.getName());
|
||||
|
||||
LOGGER.info("Found " + images.size() + " images in Docker Hub");
|
||||
for (DockerHubImage dockerHubImage : images)
|
||||
synchroniseImage(repository, dockerHubImage, listener);
|
||||
|
||||
} else {
|
||||
listener.onEvent("Skipping sync for " + repositoryName);
|
||||
}
|
||||
}
|
||||
|
||||
private void synchroniseImage(Repository repository, DockerHubImage dockerHubImage, SynchronisationListener listener) {
|
||||
|
||||
try {
|
||||
|
||||
Image image = configureImage(repository.getId(), dockerHubImage);
|
||||
|
||||
String maskedVersion = getMaskedVersion(repository.getName(), image.getName(), image.getVersionMask());
|
||||
LOGGER.debug("Updated image version using mask. Mask=" + image.getVersionMask() + ", MaskedVersion=" + maskedVersion);
|
||||
|
||||
image.withPullCount(dockerHubImage.getPullCount()).withVersion(maskedVersion);
|
||||
|
||||
listener.onEvent("Synchronising " + image);
|
||||
imageDelegate.saveImage(image);
|
||||
|
||||
} catch (SaveException e) {
|
||||
LOGGER.error("Unable to save updated image", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getMaskedVersion(String repositoryName, String imageName, String versionMask) {
|
||||
|
||||
String tag = dockerHubDelegate.fetchLatestImageTag(repositoryName, imageName);
|
||||
|
||||
if (isTagJustLatestAndNotAVersion(tag) || null == versionMask)
|
||||
return tag;
|
||||
|
||||
return extractMaskedVersion(tag, versionMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the repository name from Docker Hub, this will attempt to find it in the database. If it exists, it gets returned,
|
||||
* otherwise a new record is created (with sync disabled) and that new record is returned instead.
|
||||
*/
|
||||
private Repository configureRepository(String repositoryName) {
|
||||
|
||||
Repository repository = repositoryDelegate.findRepositoryByName(repositoryName);
|
||||
|
||||
if (isRepositoryNew(repository)) {
|
||||
|
||||
try {
|
||||
|
||||
return repositoryDelegate.saveRepository(new Repository(repositoryName));
|
||||
|
||||
} catch (SaveException e) {
|
||||
LOGGER.error("Tried to save new repository during sync but failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
return repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up the image in the database to see if it already exists. If it does, it gets returned, otherwise a base
|
||||
* image is created with just the top-level information, as the rest will get updated later.
|
||||
*/
|
||||
private Image configureImage(int repositoryId, DockerHubImage dockerHubImage) {
|
||||
|
||||
Image image = imageDelegate.findImageByRepositoryAndImageName(repositoryId, dockerHubImage.getName());
|
||||
|
||||
if (isImageNew(image)) {
|
||||
|
||||
try {
|
||||
|
||||
return imageDelegate.saveImage(new Image(repositoryId, dockerHubImage.getName()));
|
||||
|
||||
} catch (SaveException e) {
|
||||
LOGGER.error("Tried to save new image during sync but failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
private String extractMaskedVersion(String fullTag, String versionMask) {
|
||||
|
||||
Pattern pattern = Pattern.compile(versionMask);
|
||||
Matcher matcher = pattern.matcher(fullTag);
|
||||
|
||||
if (matcher.matches()) {
|
||||
|
||||
StringBuilder tagBuilder = new StringBuilder();
|
||||
|
||||
for (int groupNum = 1; groupNum <= matcher.groupCount(); groupNum++)
|
||||
tagBuilder.append(matcher.group(groupNum));
|
||||
|
||||
return tagBuilder.toString();
|
||||
}
|
||||
|
||||
return fullTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* If the top-level tag is not versioned, a mask can't be applied.
|
||||
* </p>
|
||||
*/
|
||||
private boolean isTagJustLatestAndNotAVersion(String tag) {
|
||||
return "latest".equals(tag);
|
||||
}
|
||||
|
||||
private boolean isRepositoryNew(Repository repository) {
|
||||
return null == repository;
|
||||
}
|
||||
|
||||
private boolean isImageNew(Image image) {
|
||||
return null == image;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.delegate;
|
||||
|
||||
public interface SynchronisationListener {
|
||||
void onEvent(Object eventMessage);
|
||||
}
|
||||
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.dockerhub;
|
||||
|
||||
import io.linuxserver.fleet.rest.RestClient;
|
||||
import io.linuxserver.fleet.rest.RestResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
class DockerHubAuthenticator {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DockerHubAuthenticator.class);
|
||||
|
||||
private final RestClient client;
|
||||
private final DockerHubCredentials credentials;
|
||||
|
||||
private String token;
|
||||
|
||||
DockerHubAuthenticator(DockerHubCredentials credentials, RestClient client) {
|
||||
|
||||
this.credentials = credentials;
|
||||
this.client = client;
|
||||
|
||||
refreshToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Re-authenticates with Docker Hub to obtain a fresh JWT.
|
||||
* </p>
|
||||
*
|
||||
* @return
|
||||
* The new JWT to be used in authenticated requests.
|
||||
*/
|
||||
synchronized String refreshToken() {
|
||||
|
||||
LOGGER.info("Refreshing token for Docker Hub authentication");
|
||||
|
||||
RestResponse<DockerHubTokenResponse> authenticationResponse = client.executePost(
|
||||
DockerHubV2Client.DOCKERHUB_BASE_URI + "/users/login", null, null, credentials, DockerHubTokenResponse.class);
|
||||
|
||||
if (authenticationResponse.getStatusCode() == 200) {
|
||||
|
||||
LOGGER.info("Refresh successful");
|
||||
|
||||
String token = authenticationResponse.getPayload().getToken();
|
||||
this.token = token;
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
LOGGER.info("Unable to refresh token.");
|
||||
|
||||
throw new RuntimeException("Unable to authenticate with Docker Hub. Check credentials");
|
||||
}
|
||||
|
||||
synchronized String getCurrentToken() {
|
||||
|
||||
if (null == token)
|
||||
return refreshToken();
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static class DockerHubTokenResponse {
|
||||
|
||||
private String token;
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.dockerhub;
|
||||
|
||||
import io.linuxserver.fleet.dockerhub.model.DockerHubV2Image;
|
||||
import io.linuxserver.fleet.dockerhub.model.DockerHubV2NamespaceLookupResult;
|
||||
import io.linuxserver.fleet.dockerhub.model.DockerHubV2Tag;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface DockerHubClient {
|
||||
|
||||
DockerHubV2NamespaceLookupResult fetchAllRepositories();
|
||||
|
||||
List<DockerHubV2Image> fetchImagesFromRepository(String repositoryName);
|
||||
|
||||
DockerHubV2Image fetchImageFromRepository(String repositoryName, String imageName);
|
||||
|
||||
DockerHubV2Tag fetchLatestTagForImage(String repositoryName, String imageName);
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.dockerhub;
|
||||
|
||||
public class DockerHubCredentials {
|
||||
|
||||
private final String username;
|
||||
private final String password;
|
||||
|
||||
public DockerHubCredentials(String username, String password) {
|
||||
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.dockerhub;
|
||||
|
||||
public class DockerHubException extends RuntimeException {
|
||||
|
||||
public DockerHubException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DockerHubException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.dockerhub;
|
||||
|
||||
import io.linuxserver.fleet.dockerhub.model.*;
|
||||
import io.linuxserver.fleet.rest.HttpException;
|
||||
import io.linuxserver.fleet.rest.RestClient;
|
||||
import io.linuxserver.fleet.rest.RestResponse;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Client class to interface with Docker Hub's V2 API.
|
||||
*/
|
||||
public class DockerHubV2Client implements DockerHubClient {
|
||||
|
||||
static final String DOCKERHUB_BASE_URI = "https://hub.docker.com/v2";
|
||||
|
||||
private final RestClient restClient;
|
||||
private final DockerHubAuthenticator authenticator;
|
||||
|
||||
public DockerHubV2Client(DockerHubCredentials credentials) {
|
||||
|
||||
this.restClient = new RestClient();
|
||||
this.authenticator = new DockerHubAuthenticator(credentials, restClient);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DockerHubV2NamespaceLookupResult fetchAllRepositories() {
|
||||
|
||||
try {
|
||||
|
||||
String url = DOCKERHUB_BASE_URI + "/repositories/namespaces";
|
||||
|
||||
RestResponse<DockerHubV2NamespaceLookupResult> response = doCall(url, DockerHubV2NamespaceLookupResult.class);
|
||||
|
||||
if (isResponseOK(response)) {
|
||||
return response.getPayload();
|
||||
}
|
||||
|
||||
return new DockerHubV2NamespaceLookupResult();
|
||||
|
||||
} catch (HttpException e) {
|
||||
throw new DockerHubException("Unable to get repositories", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DockerHubV2Image> fetchImagesFromRepository(String repositoryName) {
|
||||
|
||||
List<DockerHubV2Image> images = new ArrayList<>();
|
||||
|
||||
try {
|
||||
|
||||
String url = DOCKERHUB_BASE_URI + "/repositories/" + repositoryName;
|
||||
|
||||
while (url != null) {
|
||||
|
||||
RestResponse<DockerHubV2ImageListResult> response = doCall(url, DockerHubV2ImageListResult.class);
|
||||
|
||||
if (isResponseOK(response)) {
|
||||
|
||||
DockerHubV2ImageListResult payload = response.getPayload();
|
||||
|
||||
images.addAll(payload.getResults());
|
||||
url = payload.getNext();
|
||||
}
|
||||
}
|
||||
|
||||
return images;
|
||||
|
||||
} catch (HttpException e) {
|
||||
throw new DockerHubException("Unable to get images for " + repositoryName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DockerHubV2Image fetchImageFromRepository(String repositoryName, String imageName) {
|
||||
|
||||
try {
|
||||
|
||||
String absoluteUrl = DOCKERHUB_BASE_URI + "/repositories/" + repositoryName + "/" + imageName;
|
||||
|
||||
RestResponse<DockerHubV2Image> restResponse = doCall(absoluteUrl, DockerHubV2Image.class);
|
||||
return restResponse.getPayload();
|
||||
|
||||
} catch (HttpException e) {
|
||||
throw new DockerHubException("Unable to get images for " + repositoryName + "/" + imageName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DockerHubV2Tag fetchLatestTagForImage(String repositoryName, String imageName) {
|
||||
|
||||
try {
|
||||
|
||||
String absoluteUrl = DOCKERHUB_BASE_URI + "/repositories/" + repositoryName + "/" + imageName + "/tags" ;
|
||||
|
||||
RestResponse<DockerHubV2TagListResult> restResponse = doCall(absoluteUrl, DockerHubV2TagListResult.class);
|
||||
return restResponse.getPayload().getResults().get(0);
|
||||
|
||||
} catch (HttpException e) {
|
||||
throw new DockerHubException("Unable to get tags for " + repositoryName + "/" + imageName, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Attempts to call DockerHub with the currently set credentials. If they have expired, it will refresh them and try again.
|
||||
* This process will only try again once, so if the refresh resulted in another stale token, it will need to be handled.
|
||||
* </p>
|
||||
*/
|
||||
private <T> RestResponse<T> doCall(String url, Class<T> responseType) {
|
||||
|
||||
RestResponse<T> restResponse = restClient.executeGet(url, null, buildHeaders(), responseType);
|
||||
|
||||
if (isResponseUnauthorised(restResponse)) {
|
||||
|
||||
authenticator.refreshToken();
|
||||
restResponse = restClient.executeGet(url, null, buildHeaders(), responseType);
|
||||
}
|
||||
|
||||
return restResponse;
|
||||
}
|
||||
|
||||
private Map<String, String> buildHeaders() {
|
||||
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("Authorization", "JWT " + authenticator.getCurrentToken());
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
private boolean isResponseOK(RestResponse restResponse) {
|
||||
return restResponse.getStatusCode() == 200;
|
||||
}
|
||||
|
||||
private boolean isResponseUnauthorised(RestResponse restResponse) {
|
||||
return restResponse.getStatusCode() == 401;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.dockerhub.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class DockerHubV2Image {
|
||||
|
||||
@JsonProperty("user")
|
||||
private String user;
|
||||
|
||||
@JsonProperty("name")
|
||||
private String name;
|
||||
|
||||
@JsonProperty("namespace")
|
||||
private String namespace;
|
||||
|
||||
@JsonProperty("description")
|
||||
private String description;
|
||||
|
||||
@JsonProperty("star_count")
|
||||
private int starCount;
|
||||
|
||||
@JsonProperty("pull_count")
|
||||
private long pullCount;
|
||||
|
||||
@JsonProperty("last_updated")
|
||||
private String lastUpdated;
|
||||
|
||||
public final String getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public final String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public final String getNamespace() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public final int getStarCount() {
|
||||
return starCount;
|
||||
}
|
||||
|
||||
public final long getPullCount() {
|
||||
return pullCount;
|
||||
}
|
||||
|
||||
public final String getLastUpdated() {
|
||||
return lastUpdated;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.dockerhub.model;
|
||||
|
||||
public class DockerHubV2ImageListResult extends DockerHubV2ScanResult<DockerHubV2Image> {
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.dockerhub.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DockerHubV2NamespaceLookupResult {
|
||||
|
||||
@JsonProperty("namespaces")
|
||||
private List<String> namespaces = new ArrayList<>();
|
||||
|
||||
public List<String> getNamespaces() {
|
||||
return namespaces;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.dockerhub.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DockerHubV2ScanResult<T> {
|
||||
|
||||
@JsonProperty("count")
|
||||
private int count;
|
||||
|
||||
@JsonProperty("next")
|
||||
private String next;
|
||||
|
||||
@JsonProperty("previous")
|
||||
private String previous;
|
||||
|
||||
@JsonProperty("results")
|
||||
private List<T> results;
|
||||
|
||||
public final int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public final String getNext() {
|
||||
return next;
|
||||
}
|
||||
|
||||
public final String getPrevious() {
|
||||
return previous;
|
||||
}
|
||||
|
||||
public final List<T> getResults() {
|
||||
return results;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.dockerhub.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class DockerHubV2Tag {
|
||||
|
||||
@JsonProperty("name")
|
||||
private String name;
|
||||
|
||||
@JsonProperty("full_size")
|
||||
private long fullSize;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public long getFullSize() {
|
||||
return fullSize;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.dockerhub.model;
|
||||
|
||||
public class DockerHubV2TagListResult extends DockerHubV2ScanResult<DockerHubV2Tag> {
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.exception;
|
||||
|
||||
public class SaveException extends Exception {
|
||||
|
||||
public SaveException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public SaveException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
70
src/main/java/io/linuxserver/fleet/model/DockerHubImage.java
Normal file
70
src/main/java/io/linuxserver/fleet/model/DockerHubImage.java
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.model;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Fleet's specifically required structure for an image coming from DockerHub. This object
|
||||
* is common within the app, and is immune to any API changes to DockerHub itself.
|
||||
* </p>
|
||||
*/
|
||||
public class DockerHubImage {
|
||||
|
||||
private final String name;
|
||||
private final String repository;
|
||||
private final String description;
|
||||
private final int starCount;
|
||||
private final long pullCount;
|
||||
private final LocalDateTime buildDate;
|
||||
|
||||
public DockerHubImage(String name, String repository, String description, int starCount, long pullCount, LocalDateTime buildDate) {
|
||||
|
||||
this.name = name;
|
||||
this.repository = repository;
|
||||
this.description = description;
|
||||
this.starCount = starCount;
|
||||
this.pullCount = pullCount;
|
||||
this.buildDate = buildDate;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public int getStarCount() {
|
||||
return starCount;
|
||||
}
|
||||
|
||||
public long getPullCount() {
|
||||
return pullCount;
|
||||
}
|
||||
|
||||
public LocalDateTime getBuildDate() {
|
||||
return buildDate;
|
||||
}
|
||||
}
|
||||
106
src/main/java/io/linuxserver/fleet/model/Image.java
Normal file
106
src/main/java/io/linuxserver/fleet/model/Image.java
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.model;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Representation of a stored image in the Fleet database. Each image contains
|
||||
* specific information regarding its build status and pull count.
|
||||
* </p>
|
||||
*/
|
||||
public class Image extends PersistableItem<Image> {
|
||||
|
||||
private final int repositoryId;
|
||||
private final String name;
|
||||
|
||||
private String version;
|
||||
private long pullCount;
|
||||
private String versionMask;
|
||||
private boolean unstable;
|
||||
private boolean hidden;
|
||||
|
||||
public Image(Integer id, int repositoryId, String name) {
|
||||
|
||||
super(id);
|
||||
|
||||
this.name = name;
|
||||
this.repositoryId = repositoryId;
|
||||
}
|
||||
|
||||
public Image(int repositoryId, String name) {
|
||||
this(null, repositoryId, name);
|
||||
}
|
||||
|
||||
public Image withVersion(String version) {
|
||||
|
||||
this.version = version;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Image withPullCount(long pullCount) {
|
||||
|
||||
this.pullCount = pullCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Image withVersionMask(String versionMask) {
|
||||
|
||||
this.versionMask = versionMask;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Image withHidden(boolean hidden) {
|
||||
|
||||
this.hidden = hidden;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Image withUnstable(boolean unstable) {
|
||||
|
||||
this.unstable = unstable;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getRepositoryId() {
|
||||
return repositoryId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public long getPullCount() {
|
||||
return pullCount;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public String getVersionMask() {
|
||||
return versionMask;
|
||||
}
|
||||
|
||||
public boolean isUnstable() {
|
||||
return unstable;
|
||||
}
|
||||
|
||||
public boolean isHidden() {
|
||||
return hidden;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.model;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public abstract class PersistableItem<T extends PersistableItem> {
|
||||
|
||||
private Integer id;
|
||||
private LocalDateTime modifiedTime;
|
||||
|
||||
PersistableItem() { }
|
||||
|
||||
PersistableItem(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T withId(Integer id) {
|
||||
|
||||
if (null == id)
|
||||
throw new IllegalArgumentException("Item ID must not be null");
|
||||
|
||||
this.id = id;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T withModifiedTime(LocalDateTime modifiedTime) {
|
||||
|
||||
this.modifiedTime = modifiedTime;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public LocalDateTime getModifiedTime() {
|
||||
return modifiedTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||
}
|
||||
}
|
||||
62
src/main/java/io/linuxserver/fleet/model/Repository.java
Normal file
62
src/main/java/io/linuxserver/fleet/model/Repository.java
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.model;
|
||||
|
||||
public class Repository extends PersistableItem<Repository> {
|
||||
|
||||
private final String name;
|
||||
|
||||
private String versionMask;
|
||||
private boolean syncEnabled;
|
||||
|
||||
public Repository(Integer id, String name) {
|
||||
super(id);
|
||||
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Repository(String name) {
|
||||
super();
|
||||
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Repository withVersionMask(String versionMask) {
|
||||
|
||||
this.versionMask = versionMask;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Repository withSyncEnabled(boolean syncEnabled) {
|
||||
|
||||
this.syncEnabled = syncEnabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getVersionMask() {
|
||||
return versionMask;
|
||||
}
|
||||
|
||||
public boolean isSyncEnabled() {
|
||||
return syncEnabled;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.model;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class RepositoryWithImages {
|
||||
|
||||
private final Repository repository;
|
||||
private final List<Image> images;
|
||||
|
||||
public RepositoryWithImages(Repository repository, List<Image> images) {
|
||||
|
||||
this.repository = repository;
|
||||
this.images = Collections.unmodifiableList(images);
|
||||
}
|
||||
|
||||
public Repository getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
public List<Image> getImages() {
|
||||
return images;
|
||||
}
|
||||
|
||||
public boolean isEveryImageStable() {
|
||||
return images.stream().noneMatch(Image::isUnstable);
|
||||
}
|
||||
}
|
||||
56
src/main/java/io/linuxserver/fleet/model/api/ApiImage.java
Normal file
56
src/main/java/io/linuxserver/fleet/model/api/ApiImage.java
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.model.api;
|
||||
|
||||
import io.linuxserver.fleet.model.Image;
|
||||
|
||||
public class ApiImage {
|
||||
|
||||
private String name;
|
||||
private long pullCount;
|
||||
private String version;
|
||||
private boolean stable;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public long getPullCount() {
|
||||
return pullCount;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public boolean isStable() {
|
||||
return stable;
|
||||
}
|
||||
|
||||
public static ApiImage fromImage(Image image) {
|
||||
|
||||
ApiImage apiImage = new ApiImage();
|
||||
|
||||
apiImage.name = image.getName();
|
||||
apiImage.pullCount = image.getPullCount();
|
||||
apiImage.version = image.getVersion();
|
||||
apiImage.stable = !image.isUnstable();
|
||||
|
||||
return apiImage;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.model.api;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ApiImagesWithTotalCount {
|
||||
|
||||
private final long totalPullCount;
|
||||
private final Map<String, List<ApiImage>> repositories;
|
||||
|
||||
public ApiImagesWithTotalCount(long totalPullCount, Map<String, List<ApiImage>> repositories) {
|
||||
|
||||
this.totalPullCount = totalPullCount;
|
||||
this.repositories = repositories;
|
||||
}
|
||||
|
||||
public long getTotalPullCount() {
|
||||
return totalPullCount;
|
||||
}
|
||||
|
||||
public Map<String, List<ApiImage>> getRepositories() {
|
||||
return repositories;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.model.api;
|
||||
|
||||
public class ApiResponse<T> {
|
||||
|
||||
private final String status;
|
||||
private final T data;
|
||||
|
||||
public ApiResponse(String status, T data) {
|
||||
|
||||
this.status = status;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public T getData() {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.model.api;
|
||||
|
||||
public class FleetApiException extends RuntimeException {
|
||||
|
||||
private final int statusCode;
|
||||
|
||||
public FleetApiException(int statusCode, String message) {
|
||||
super(message);
|
||||
|
||||
this.statusCode = statusCode;
|
||||
}
|
||||
|
||||
public FleetApiException(int statusCode, String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
|
||||
this.statusCode = statusCode;
|
||||
}
|
||||
|
||||
public int getStatusCode() {
|
||||
return statusCode;
|
||||
}
|
||||
}
|
||||
29
src/main/java/io/linuxserver/fleet/rest/HttpException.java
Normal file
29
src/main/java/io/linuxserver/fleet/rest/HttpException.java
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.rest;
|
||||
|
||||
public class HttpException extends RuntimeException {
|
||||
|
||||
public HttpException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public HttpException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
154
src/main/java/io/linuxserver/fleet/rest/RestClient.java
Normal file
154
src/main/java/io/linuxserver/fleet/rest/RestClient.java
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.rest;
|
||||
|
||||
import io.linuxserver.fleet.rest.marshalling.JacksonMarshallingStrategy;
|
||||
import io.linuxserver.fleet.rest.marshalling.MarshallingStrategy;
|
||||
import io.linuxserver.fleet.rest.proxy.LazyLoadPayloadProxy;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.StatusLine;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.apache.http.client.protocol.HttpClientContext;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Simple wrapper for base HttpClient, providing an easier way to marshall/unmarshall payloads
|
||||
* </p>
|
||||
*/
|
||||
public class RestClient {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(RestClient.class);
|
||||
|
||||
private final CloseableHttpClient client;
|
||||
private final HttpClientContext clientContext;
|
||||
|
||||
private MarshallingStrategy marshallingStrategy;
|
||||
|
||||
public RestClient() {
|
||||
|
||||
setMarshallingStrategy(new JacksonMarshallingStrategy());
|
||||
client = HttpClients.custom().setConnectionManager(new PoolingHttpClientConnectionManager()).build();
|
||||
clientContext = HttpClientContext.create();
|
||||
}
|
||||
|
||||
private void setMarshallingStrategy(MarshallingStrategy marshallingStrategy) {
|
||||
|
||||
LOGGER.debug("Configuring RestClient with " + marshallingStrategy.getClass().getName());
|
||||
this.marshallingStrategy = marshallingStrategy;
|
||||
}
|
||||
|
||||
public <T> RestResponse<T> executeGet(String url, Map<String, String> queryParameters, Map<String, String> headers, Class<T> responseType) {
|
||||
|
||||
try {
|
||||
|
||||
return executeBaseRequest(responseType, headers, new HttpGet(url + parseQueryParameters(queryParameters)));
|
||||
|
||||
} catch (IOException e) {
|
||||
|
||||
LOGGER.error("Unable to perform GET", e);
|
||||
throw new HttpException("Unable to perform GET", e);
|
||||
}
|
||||
}
|
||||
|
||||
public <T> RestResponse<T> executePost(String url, Map<String, String> queryParameters, Map<String, String> headers, Object payload, Class<T> responseType) {
|
||||
|
||||
|
||||
try {
|
||||
|
||||
HttpPost post = new HttpPost(url + parseQueryParameters(queryParameters));
|
||||
post.setEntity(new StringEntity(marshallingStrategy.marshall(payload), Charset.forName("UTF-8")));
|
||||
post.setHeader("Content-Type", marshallingStrategy.getContentType());
|
||||
|
||||
return executeBaseRequest(responseType, headers, post);
|
||||
|
||||
} catch (IOException e) {
|
||||
|
||||
LOGGER.error("Unable to perform GET", e);
|
||||
throw new HttpException("Unable to perform GET", e);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> RestResponse<T> executeBaseRequest(Class<T> responseType, Map<String, String> headers, HttpRequestBase request) throws IOException {
|
||||
|
||||
LOGGER.debug("url : " + request.getURI().toString());
|
||||
LOGGER.debug("headers : " + headers);
|
||||
|
||||
if (headers != null) {
|
||||
|
||||
for (Map.Entry<String, String> header : headers.entrySet())
|
||||
request.setHeader(header.getKey(), header.getValue());
|
||||
}
|
||||
|
||||
LOGGER.debug("Executing.");
|
||||
try (CloseableHttpResponse response = client.execute(request, clientContext)) {
|
||||
|
||||
StatusLine statusLine = response.getStatusLine();
|
||||
LOGGER.debug("Response status: " + statusLine);
|
||||
|
||||
int statusCode = statusLine.getStatusCode();
|
||||
|
||||
HttpEntity content = response.getEntity();
|
||||
|
||||
String responseBody = null;
|
||||
if (null != content) {
|
||||
|
||||
responseBody = EntityUtils.toString(content);
|
||||
LOGGER.debug("Parsed response payload: " + responseBody);
|
||||
}
|
||||
|
||||
if (null != responseBody)
|
||||
return new RestResponse<>(new LazyLoadPayloadProxy<>(marshallingStrategy, responseBody, responseType), statusCode);
|
||||
|
||||
return new RestResponse<>(statusCode);
|
||||
|
||||
} finally {
|
||||
request.releaseConnection();
|
||||
}
|
||||
}
|
||||
|
||||
private String parseQueryParameters(Map<String, String> queryParameters) {
|
||||
|
||||
if (null != queryParameters) {
|
||||
|
||||
StringBuilder builtParameterString = new StringBuilder("?");
|
||||
|
||||
for (Map.Entry<String, String> param : queryParameters.entrySet())
|
||||
builtParameterString.append(param.getKey()).append("=").append(param.getValue()).append("&");
|
||||
|
||||
builtParameterString.setLength(builtParameterString.length() - 1);
|
||||
|
||||
return builtParameterString.toString();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
44
src/main/java/io/linuxserver/fleet/rest/RestResponse.java
Normal file
44
src/main/java/io/linuxserver/fleet/rest/RestResponse.java
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.rest;
|
||||
|
||||
import io.linuxserver.fleet.rest.proxy.PayloadProxy;
|
||||
|
||||
public class RestResponse<T> {
|
||||
|
||||
private PayloadProxy<T> payloadProxy;
|
||||
private int statusCode;
|
||||
|
||||
RestResponse(int statusCode) {
|
||||
this(null, statusCode);
|
||||
}
|
||||
|
||||
RestResponse(PayloadProxy<T> payloadProxy, int statusCode) {
|
||||
|
||||
this.payloadProxy = payloadProxy;
|
||||
this.statusCode = statusCode;
|
||||
}
|
||||
|
||||
public T getPayload() {
|
||||
return payloadProxy.get();
|
||||
}
|
||||
|
||||
public int getStatusCode() {
|
||||
return statusCode;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.rest.marshalling;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Jackson JSON implementation of the marshalling strategy. This will convert incoming
|
||||
* and outgoing messages formatted in JSON.
|
||||
*/
|
||||
public class JacksonMarshallingStrategy implements MarshallingStrategy {
|
||||
|
||||
private static final ObjectMapper OBJECT_MAPPER;
|
||||
static {
|
||||
|
||||
OBJECT_MAPPER = new ObjectMapper();
|
||||
OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T unmarshall(String value, Class<T> classType) throws IOException {
|
||||
return OBJECT_MAPPER.readValue(value, classType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String marshall(Object value) throws JsonProcessingException {
|
||||
return OBJECT_MAPPER.writeValueAsString(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentType() {
|
||||
return "application/json";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package io.linuxserver.fleet.rest.marshalling;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface MarshallingStrategy {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Converts a given string value into its representative object type.
|
||||
* </p>
|
||||
* @param value
|
||||
* The value to convert to an object
|
||||
* @param classType
|
||||
* The object class definition
|
||||
* @return
|
||||
* The converted object
|
||||
*/
|
||||
<T> T unmarshall(String value, Class<T> classType) throws IOException;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Converts an object into a single representative string value.
|
||||
* </p>
|
||||
* @param value
|
||||
* The object to convert
|
||||
* @return
|
||||
* The result of the conversion
|
||||
*/
|
||||
String marshall(Object value) throws IOException;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The content type of the payloads represented by this strategy.
|
||||
* </p>
|
||||
*/
|
||||
String getContentType();
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.rest.proxy;
|
||||
|
||||
import io.linuxserver.fleet.rest.HttpException;
|
||||
import io.linuxserver.fleet.rest.marshalling.MarshallingStrategy;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class LazyLoadPayloadProxy<T> implements PayloadProxy<T> {
|
||||
|
||||
private final MarshallingStrategy marshallingStrategy;
|
||||
private final String payload;
|
||||
private final Class<T> payloadType;
|
||||
|
||||
public LazyLoadPayloadProxy(MarshallingStrategy marshallingStrategy, String payload, Class<T> payloadType) {
|
||||
|
||||
this.marshallingStrategy = marshallingStrategy;
|
||||
this.payload = payload;
|
||||
this.payloadType = payloadType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
|
||||
try {
|
||||
return marshallingStrategy.unmarshall(payload, payloadType);
|
||||
} catch (IOException e) {
|
||||
throw new HttpException("Unable to unmarshall response payload", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.rest.proxy;
|
||||
|
||||
public interface PayloadProxy<T> {
|
||||
|
||||
T get();
|
||||
}
|
||||
58
src/main/java/io/linuxserver/fleet/thread/FleetTask.java
Normal file
58
src/main/java/io/linuxserver/fleet/thread/FleetTask.java
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.thread;
|
||||
|
||||
public abstract class FleetTask implements Runnable {
|
||||
|
||||
private TaskListener taskListener;
|
||||
|
||||
public void setTaskListener(TaskListener listener) {
|
||||
this.taskListener = listener;
|
||||
}
|
||||
|
||||
protected TaskListener getTaskListener() {
|
||||
return taskListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
onStart(toString() + " has started.");
|
||||
executeTask();
|
||||
onEnd(toString() + " has finished.");
|
||||
}
|
||||
|
||||
protected abstract void executeTask();
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName();
|
||||
}
|
||||
|
||||
private void onStart(String message) {
|
||||
|
||||
if (taskListener != null)
|
||||
taskListener.onTaskStart(message);
|
||||
}
|
||||
|
||||
private void onEnd(String message) {
|
||||
|
||||
if (taskListener != null)
|
||||
taskListener.onTaskEnd(message);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.thread;
|
||||
|
||||
import io.linuxserver.fleet.delegate.SynchronisationDelegate;
|
||||
|
||||
public class SynchroniseAllRepositoriesTask extends FleetTask {
|
||||
|
||||
private final SynchronisationDelegate synchronisationDelegate;
|
||||
|
||||
public SynchroniseAllRepositoriesTask(SynchronisationDelegate synchronisationDelegate) {
|
||||
this.synchronisationDelegate = synchronisationDelegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void executeTask() {
|
||||
|
||||
TaskListener listener = getTaskListener();
|
||||
|
||||
if (listener == null)
|
||||
synchronisationDelegate.synchronise();
|
||||
|
||||
else
|
||||
synchronisationDelegate.synchronise((event) -> listener.onTaskOutput(event.toString()));
|
||||
}
|
||||
}
|
||||
10
src/main/java/io/linuxserver/fleet/thread/TaskListener.java
Normal file
10
src/main/java/io/linuxserver/fleet/thread/TaskListener.java
Normal file
@ -0,0 +1,10 @@
|
||||
package io.linuxserver.fleet.thread;
|
||||
|
||||
public interface TaskListener {
|
||||
|
||||
void onTaskStart(String message);
|
||||
|
||||
void onTaskOutput(String output);
|
||||
|
||||
void onTaskEnd(String message);
|
||||
}
|
||||
46
src/main/java/io/linuxserver/fleet/thread/TaskManager.java
Normal file
46
src/main/java/io/linuxserver/fleet/thread/TaskManager.java
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.thread;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class TaskManager {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(TaskManager.class);
|
||||
|
||||
private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(10);
|
||||
|
||||
public void scheduleRecurringTask(FleetTask task, int interval, TimeUnit timeUnit) {
|
||||
|
||||
LOGGER.info("Scheduling task " + task);
|
||||
executorService.scheduleAtFixedRate(task, 0, interval, timeUnit);
|
||||
}
|
||||
|
||||
public void runTaskOnce(FleetTask task) {
|
||||
start(task);
|
||||
}
|
||||
|
||||
private void start(FleetTask task) {
|
||||
new Thread(task).start();
|
||||
}
|
||||
}
|
||||
40
src/main/java/io/linuxserver/fleet/web/JsonTransformer.java
Normal file
40
src/main/java/io/linuxserver/fleet/web/JsonTransformer.java
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.web;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import spark.ResponseTransformer;
|
||||
|
||||
public class JsonTransformer implements ResponseTransformer {
|
||||
|
||||
private static final ObjectMapper OBJECT_MAPPER;
|
||||
static {
|
||||
OBJECT_MAPPER = new ObjectMapper();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String render(Object model) {
|
||||
|
||||
try {
|
||||
return OBJECT_MAPPER.writeValueAsString(model);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
package io.linuxserver.fleet.web;
|
||||
|
||||
public interface SessionAttribute {
|
||||
|
||||
String USER = "io.linuxserver.fleet.web.SessionAttribute.USER";
|
||||
}
|
||||
97
src/main/java/io/linuxserver/fleet/web/WebServer.java
Normal file
97
src/main/java/io/linuxserver/fleet/web/WebServer.java
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.web;
|
||||
|
||||
import io.linuxserver.fleet.auth.AuthenticatedUser;
|
||||
import io.linuxserver.fleet.model.api.ApiResponse;
|
||||
import io.linuxserver.fleet.model.api.FleetApiException;
|
||||
import spark.Route;
|
||||
import spark.RouteGroup;
|
||||
import spark.Session;
|
||||
import spark.TemplateViewRoute;
|
||||
import spark.template.freemarker.FreeMarkerEngine;
|
||||
|
||||
import static spark.Spark.*;
|
||||
|
||||
public class WebServer {
|
||||
|
||||
private final int appPort;
|
||||
|
||||
public WebServer(int appPort) {
|
||||
this.appPort = appPort;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
|
||||
port(appPort);
|
||||
|
||||
staticFiles.location("/assets");
|
||||
staticFiles.expireTime(600);
|
||||
|
||||
path("/admin", configureAuthorisationRoute(""));
|
||||
path("/admin", configureAuthorisationRoute("/repositories"));
|
||||
path("/admin", configureAuthorisationRoute("/images"));
|
||||
path("/admin", configureAuthorisationRoute("/manageImage"));
|
||||
path("/admin", configureAuthorisationRoute("/manageRepository"));
|
||||
|
||||
after("/api/v1/*", (request, response) -> {
|
||||
response.header("Content-Type", "application/json");
|
||||
});
|
||||
|
||||
exception(FleetApiException.class, (exception, request, response) -> {
|
||||
|
||||
response.body(new JsonTransformer().render(new ApiResponse<>("ERROR", exception.getMessage())));
|
||||
response.header("Content-Type", "application/json");
|
||||
response.status(exception.getStatusCode());
|
||||
});
|
||||
}
|
||||
|
||||
public void addPage(String path, TemplateViewRoute page) {
|
||||
get(path, page, new FreeMarkerEngine());
|
||||
}
|
||||
|
||||
public void addPostRoute(String path, Route route) {
|
||||
post(path, route);
|
||||
}
|
||||
|
||||
public void addGetApi(String path, Route route) {
|
||||
get(path, route, new JsonTransformer());
|
||||
}
|
||||
|
||||
public void addPostApi(String path, Route route) {
|
||||
post(path, route, new JsonTransformer());
|
||||
}
|
||||
|
||||
private RouteGroup configureAuthorisationRoute(String path) {
|
||||
|
||||
return () -> before(path, (request, response) -> {
|
||||
|
||||
Session session = request.session(false);
|
||||
|
||||
if (null == session)
|
||||
response.redirect("/admin/login");
|
||||
|
||||
else {
|
||||
|
||||
AuthenticatedUser user = session.attribute(SessionAttribute.USER);
|
||||
if (null == user)
|
||||
response.redirect("/admin/login");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
60
src/main/java/io/linuxserver/fleet/web/pages/HomePage.java
Normal file
60
src/main/java/io/linuxserver/fleet/web/pages/HomePage.java
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.web.pages;
|
||||
|
||||
import io.linuxserver.fleet.delegate.ImageDelegate;
|
||||
import io.linuxserver.fleet.delegate.RepositoryDelegate;
|
||||
import io.linuxserver.fleet.model.Repository;
|
||||
import io.linuxserver.fleet.model.RepositoryWithImages;
|
||||
import spark.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class HomePage extends WebPage {
|
||||
|
||||
private final RepositoryDelegate repositoryDelegate;
|
||||
private final ImageDelegate imageDelegate;
|
||||
|
||||
public HomePage(RepositoryDelegate repositoryDelegate, ImageDelegate imageDelegate) {
|
||||
|
||||
this.repositoryDelegate = repositoryDelegate;
|
||||
this.imageDelegate = imageDelegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ModelAndView handle(Request request) {
|
||||
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
|
||||
List<RepositoryWithImages> populatedRepositories = new ArrayList<>();
|
||||
|
||||
List<Repository> repositories = repositoryDelegate.fetchAllRepositories();
|
||||
for (Repository repository : repositories) {
|
||||
|
||||
if (repository.isSyncEnabled())
|
||||
populatedRepositories.add(new RepositoryWithImages(repository, imageDelegate.fetchImagesByRepository(repository.getId())));
|
||||
}
|
||||
|
||||
model.put("populatedRepositories", populatedRepositories);
|
||||
|
||||
return new ModelAndView(model, "home.ftl");
|
||||
}
|
||||
}
|
||||
40
src/main/java/io/linuxserver/fleet/web/pages/LoginPage.java
Normal file
40
src/main/java/io/linuxserver/fleet/web/pages/LoginPage.java
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.web.pages;
|
||||
|
||||
import spark.ModelAndView;
|
||||
import spark.Request;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class LoginPage extends WebPage {
|
||||
|
||||
@Override
|
||||
protected ModelAndView handle(Request request) {
|
||||
|
||||
String failFlag = request.params("fail");
|
||||
|
||||
Map<Object, Object> model = new HashMap<>();
|
||||
|
||||
if (null != failFlag)
|
||||
model.put("authFailure", failFlag);
|
||||
|
||||
return new ModelAndView(model, "login.ftl");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.web.pages;
|
||||
|
||||
import io.linuxserver.fleet.delegate.RepositoryDelegate;
|
||||
import spark.ModelAndView;
|
||||
import spark.Request;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ManageRepositoriesPage extends WebPage {
|
||||
|
||||
private final RepositoryDelegate repositoryDelegate;
|
||||
|
||||
public ManageRepositoriesPage(RepositoryDelegate repositoryDelegate) {
|
||||
this.repositoryDelegate = repositoryDelegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ModelAndView handle(Request request) {
|
||||
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
model.put("repositories", repositoryDelegate.fetchAllRepositories());
|
||||
return new ModelAndView(model, "admin.ftl");
|
||||
}
|
||||
}
|
||||
41
src/main/java/io/linuxserver/fleet/web/pages/WebPage.java
Normal file
41
src/main/java/io/linuxserver/fleet/web/pages/WebPage.java
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.web.pages;
|
||||
|
||||
import io.linuxserver.fleet.web.SessionAttribute;
|
||||
import spark.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class WebPage implements TemplateViewRoute {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public ModelAndView handle(Request request, Response response) {
|
||||
|
||||
ModelAndView modelAndView = handle(request);
|
||||
|
||||
Session session = request.session(false);
|
||||
if (null != session && null != session.attribute(SessionAttribute.USER))
|
||||
((Map<String, Object>) modelAndView.getModel()).put("__AUTHENTICATED_USER", session.attribute(SessionAttribute.USER));
|
||||
|
||||
return modelAndView;
|
||||
}
|
||||
|
||||
protected abstract ModelAndView handle(Request request);
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.web.routes;
|
||||
|
||||
import io.linuxserver.fleet.delegate.ImageDelegate;
|
||||
import io.linuxserver.fleet.delegate.RepositoryDelegate;
|
||||
import io.linuxserver.fleet.model.*;
|
||||
import io.linuxserver.fleet.model.api.ApiImage;
|
||||
import io.linuxserver.fleet.model.api.ApiImagesWithTotalCount;
|
||||
import io.linuxserver.fleet.model.api.ApiResponse;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
import spark.Route;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class AllImagesApi implements Route {
|
||||
|
||||
private final RepositoryDelegate repositoryDelegate;
|
||||
private final ImageDelegate imageDelegate;
|
||||
|
||||
public AllImagesApi(RepositoryDelegate repositoryDelegate, ImageDelegate imageDelegate) {
|
||||
|
||||
this.repositoryDelegate = repositoryDelegate;
|
||||
this.imageDelegate = imageDelegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handle(Request request, Response response) {
|
||||
|
||||
Map<String, List<ApiImage>> mappedImages = new HashMap<>();
|
||||
long totalCount = 0;
|
||||
|
||||
List<Repository> repositories = repositoryDelegate.fetchAllRepositories();
|
||||
for (Repository repository : repositories) {
|
||||
|
||||
if (repository.isSyncEnabled()) {
|
||||
|
||||
List<Image> savedImages = imageDelegate.fetchImagesByRepository(repository.getId());
|
||||
List<ApiImage> apiImages = new ArrayList<>();
|
||||
|
||||
for (Image savedImage : savedImages) {
|
||||
|
||||
if (!savedImage.isHidden()) {
|
||||
|
||||
totalCount += savedImage.getPullCount();
|
||||
apiImages.add(ApiImage.fromImage(savedImage));
|
||||
}
|
||||
}
|
||||
|
||||
mappedImages.put(repository.getName(), apiImages);
|
||||
}
|
||||
}
|
||||
|
||||
return new ApiResponse<>("OK", new ApiImagesWithTotalCount(totalCount, mappedImages));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.web.routes;
|
||||
|
||||
import io.linuxserver.fleet.auth.AuthenticationResult;
|
||||
import io.linuxserver.fleet.delegate.AuthenticationDelegate;
|
||||
import io.linuxserver.fleet.web.SessionAttribute;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
import spark.Route;
|
||||
import spark.Session;
|
||||
|
||||
public class LoginRoute implements Route {
|
||||
|
||||
private final AuthenticationDelegate authenticationDelegate;
|
||||
|
||||
public LoginRoute(AuthenticationDelegate authenticationDelegate) {
|
||||
this.authenticationDelegate = authenticationDelegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handle(Request request, Response response) {
|
||||
|
||||
Session session = request.session(false);
|
||||
if (isUserAlreadyLoggedIn(session)) {
|
||||
|
||||
response.redirect("/");
|
||||
return null;
|
||||
}
|
||||
|
||||
String username = request.queryParams("username");
|
||||
String password = request.queryParams("password");
|
||||
|
||||
AuthenticationResult authResult = authenticationDelegate.authenticate(username, password);
|
||||
if (!authResult.isAuthenticated()) {
|
||||
|
||||
response.redirect("/admin/login?fail=true");
|
||||
return null;
|
||||
}
|
||||
|
||||
final Session newSession = request.session();
|
||||
newSession.attribute(SessionAttribute.USER, authResult.getUser());
|
||||
|
||||
response.redirect("/");
|
||||
return "OK";
|
||||
}
|
||||
|
||||
private boolean isUserAlreadyLoggedIn(Session session) {
|
||||
return null != session && null != session.attribute(SessionAttribute.USER);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.web.routes;
|
||||
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
import spark.Route;
|
||||
import spark.Session;
|
||||
|
||||
public class LogoutRoute implements Route {
|
||||
|
||||
@Override
|
||||
public Object handle(Request request, Response response) {
|
||||
|
||||
Session session = request.session(false);
|
||||
|
||||
if (null != session)
|
||||
session.invalidate();
|
||||
|
||||
response.redirect("/");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.web.routes;
|
||||
|
||||
import io.linuxserver.fleet.delegate.ImageDelegate;
|
||||
import io.linuxserver.fleet.exception.SaveException;
|
||||
import io.linuxserver.fleet.model.Image;
|
||||
import io.linuxserver.fleet.model.api.ApiResponse;
|
||||
import io.linuxserver.fleet.model.api.FleetApiException;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
import spark.Route;
|
||||
|
||||
public class ManageImageApi implements Route {
|
||||
|
||||
private final ImageDelegate imageDelegate;
|
||||
|
||||
public ManageImageApi(ImageDelegate imageDelegate) {
|
||||
this.imageDelegate = imageDelegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handle(Request request, Response response) {
|
||||
|
||||
try {
|
||||
|
||||
int imageId = Integer.parseInt(request.queryParams("imageId"));
|
||||
|
||||
Image image = imageDelegate.fetchImage(imageId);
|
||||
if (null == image) {
|
||||
|
||||
response.status(404);
|
||||
response.header("Content-Type", "application/json");
|
||||
return new ApiResponse<>("Error", "Image not found.");
|
||||
}
|
||||
|
||||
Action action = Action.valueOf(request.queryParams("action"));
|
||||
switch (action) {
|
||||
|
||||
case SHOW:
|
||||
case HIDE:
|
||||
image.withHidden(Action.HIDE.equals(action));
|
||||
break;
|
||||
case STABLE:
|
||||
case UNSTABLE:
|
||||
image.withUnstable(Action.UNSTABLE.equals(action));
|
||||
break;
|
||||
}
|
||||
|
||||
imageDelegate.saveImage(image);
|
||||
|
||||
response.header("Content-Type", "application/json");
|
||||
return new ApiResponse<>("OK", "Image updated.");
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new FleetApiException(500, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public enum Action {
|
||||
SHOW, HIDE, MASK, STABLE, UNSTABLE
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.linuxserver.fleet.web.routes;
|
||||
|
||||
import io.linuxserver.fleet.delegate.RepositoryDelegate;
|
||||
import io.linuxserver.fleet.model.Repository;
|
||||
import io.linuxserver.fleet.model.api.ApiResponse;
|
||||
import io.linuxserver.fleet.model.api.FleetApiException;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
import spark.Route;
|
||||
|
||||
public class ManageRepositoryApi implements Route {
|
||||
|
||||
private final RepositoryDelegate repositoryDelegate;
|
||||
|
||||
public ManageRepositoryApi(RepositoryDelegate repositoryDelegate) {
|
||||
this.repositoryDelegate = repositoryDelegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handle(Request request, Response response) {
|
||||
|
||||
try {
|
||||
|
||||
int repositoryId = Integer.parseInt(request.queryParams("repositoryId"));
|
||||
|
||||
Repository repository = repositoryDelegate.fetchRepository(repositoryId);
|
||||
if (null == repository) {
|
||||
|
||||
response.status(404);
|
||||
response.header("Content-Type", "application/json");
|
||||
return new ApiResponse<>("Error", "Repository not found.");
|
||||
}
|
||||
|
||||
Action action = Action.valueOf(request.queryParams("action"));
|
||||
switch (action) {
|
||||
|
||||
case ENABLE_SYNC:
|
||||
case DISABLE_SYNC:
|
||||
repository.withSyncEnabled(Action.ENABLE_SYNC.equals(action));
|
||||
break;
|
||||
|
||||
case MASK:
|
||||
String versionMask = cleanParam(request.queryParams("versionMask"));
|
||||
repository.withVersionMask(versionMask);
|
||||
break;
|
||||
}
|
||||
|
||||
repositoryDelegate.saveRepository(repository);
|
||||
|
||||
response.header("Content-Type", "application/json");
|
||||
return new ApiResponse<>("OK", "Repository updated.");
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new FleetApiException(500, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private String cleanParam(String param) {
|
||||
|
||||
if ("".equalsIgnoreCase(param))
|
||||
return null;
|
||||
|
||||
return param;
|
||||
}
|
||||
|
||||
public enum Action {
|
||||
ENABLE_SYNC, DISABLE_SYNC, MASK
|
||||
}
|
||||
}
|
||||
7
src/main/resources/assets/css/bootstrap.min.css
vendored
Normal file
7
src/main/resources/assets/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
src/main/resources/assets/css/bootstrap.min.css.map
Normal file
1
src/main/resources/assets/css/bootstrap.min.css.map
Normal file
File diff suppressed because one or more lines are too long
204
src/main/resources/assets/css/fleet.css
Normal file
204
src/main/resources/assets/css/fleet.css
Normal file
@ -0,0 +1,204 @@
|
||||
body {
|
||||
color: #666;
|
||||
font-family: 'Nunito', sans-serif !important;
|
||||
font-size: 14px;
|
||||
/*background-color: #f2f3f8;*/
|
||||
}
|
||||
|
||||
a {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5 {
|
||||
color: #333;
|
||||
font-family: 'Nunito', sans-serif;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-family: 'Pacifico', cursive;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.navbar-brand .small {
|
||||
|
||||
font-family: 'Nunito', sans-serif;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.navbar-darkish {
|
||||
|
||||
background-color: #cdcdcd;
|
||||
-webkit-box-shadow: 0px 0px 14px 0px rgba(82,63,105,0.05);
|
||||
-moz-box-shadow: 0px 0px 14px 0px rgba(82,63,105,0.05);
|
||||
box-shadow: 0px 0px 14px 0px rgba(82,63,105,0.05);
|
||||
}
|
||||
|
||||
.navbar-white {
|
||||
|
||||
box-shadow: 0 3px 8px 0 rgba(116, 129, 141, 0.1);
|
||||
border-bottom: 1px solid #d4dadf;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.table thead tr th {
|
||||
|
||||
border-top: 0;
|
||||
padding-bottom: 0;
|
||||
text-transform: uppercase;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.table tbody tr td {
|
||||
border-color: #eaeaea;
|
||||
}
|
||||
|
||||
.table tbody tr.hidden-image, .table tbody tr.hidden-image a, .table tbody tr.hidden-image code {
|
||||
color: rgba(0, 0, 0, 0.3)
|
||||
}
|
||||
|
||||
.table tbody tr td.image-name {
|
||||
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
code {
|
||||
|
||||
background-color: #efefef;
|
||||
border-radius: 2px;
|
||||
color: #333;
|
||||
font-size: 12px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.container--image-list {
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
/*
|
||||
.container--white {
|
||||
|
||||
background-color: #FFF;
|
||||
|
||||
-webkit-box-shadow: 0px 0px 14px 0px rgba(82,63,105,0.05);
|
||||
-moz-box-shadow: 0px 0px 14px 0px rgba(82,63,105,0.05);
|
||||
box-shadow: 0px 0px 14px 0px rgba(82,63,105,0.05);
|
||||
|
||||
border-radius: 5px;
|
||||
}
|
||||
*/
|
||||
|
||||
.number, .version-mask {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.col--shaded {
|
||||
|
||||
background-color: #fff;
|
||||
border-radius: 1px;
|
||||
border: 1px solid #efefef;
|
||||
}
|
||||
|
||||
.btn-xsm {
|
||||
|
||||
padding: .20rem .45rem;
|
||||
font-size: .700rem;
|
||||
line-height: 1.4;
|
||||
border-radius: .2rem;
|
||||
}
|
||||
|
||||
.btn-clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tablesorter-header {
|
||||
cursor: pointer;
|
||||
|
||||
}
|
||||
|
||||
.tablesorter-header:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/*
|
||||
*****
|
||||
iOS Switches copied from: https://codepen.io/aorcsik/pen/OPMyQp
|
||||
*****
|
||||
*/
|
||||
|
||||
/* The switch - the box around the slider */
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 44px;
|
||||
height: 26px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Hide default HTML checkbox */
|
||||
.switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/* The slider */
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #ccc;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
left: 4px;
|
||||
bottom: 4px;
|
||||
background-color: white;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
input:checked + .slider {
|
||||
background-color: #2196F3;
|
||||
}
|
||||
|
||||
input:focus + .slider {
|
||||
box-shadow: 0 0 1px #2196F3;
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
-webkit-transform: translateX(18px);
|
||||
-ms-transform: translateX(18px);
|
||||
transform: translateX(18px);
|
||||
}
|
||||
|
||||
.slider.round {
|
||||
border-radius: 26px;
|
||||
}
|
||||
|
||||
.slider.round:before {
|
||||
border-radius: 50%;
|
||||
}
|
||||
5
src/main/resources/assets/css/fontawesome-all.css
Normal file
5
src/main/resources/assets/css/fontawesome-all.css
Normal file
File diff suppressed because one or more lines are too long
193
src/main/resources/assets/css/tablesorter.css
Normal file
193
src/main/resources/assets/css/tablesorter.css
Normal file
@ -0,0 +1,193 @@
|
||||
/**
|
||||
* Bootstrap theme v3.x
|
||||
*
|
||||
* WARNING!... once a stable Bootstrap v4.x is released,
|
||||
* this file will be removed; use theme.bootstrap_3.css
|
||||
*/
|
||||
.tablesorter-bootstrap {
|
||||
width: 100%;
|
||||
}
|
||||
.tablesorter-bootstrap thead th,
|
||||
.tablesorter-bootstrap thead td,
|
||||
.tablesorter-bootstrap tfoot th,
|
||||
.tablesorter-bootstrap tfoot td {
|
||||
font: 14px/20px Arial, Sans-serif;
|
||||
font-weight: bold;
|
||||
padding: 4px;
|
||||
margin: 0 0 18px;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.tablesorter-bootstrap .tablesorter-header {
|
||||
cursor: pointer;
|
||||
}
|
||||
.tablesorter-bootstrap .sorter-false {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.tablesorter-bootstrap .tablesorter-header.sorter-false i.tablesorter-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tablesorter-bootstrap .tablesorter-header-inner {
|
||||
position: relative;
|
||||
padding: 4px 18px 4px 4px;
|
||||
}
|
||||
.tablesorter-bootstrap .sorter-false .tablesorter-header-inner {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
/* bootstrap uses <i> for icons */
|
||||
.tablesorter-bootstrap .tablesorter-header i.tablesorter-icon {
|
||||
font-size: 11px;
|
||||
position: absolute;
|
||||
right: 2px;
|
||||
top: 50%;
|
||||
margin-top: -7px; /* half the icon height; older IE doesn't like this */
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background-repeat: no-repeat;
|
||||
line-height: 14px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* black unsorted icon */
|
||||
.tablesorter-bootstrap .bootstrap-icon-unsorted {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOCAYAAAD5YeaVAAAA20lEQVR4AWJABpKSkoxALCstLb0aUAsZaCAMhVEY6B0amx8YZWDDEDSBa2AGe7XeIiAAClYwVGBvsAcIllsf/mvcC9DgOOd8h90fxWvngVEUbZIkuWRZZlE8eQjcisgZMM9zi+LJ6ZfwegmWZflZDugdHMfxTcGqql7TNBlUB/QObtv2VBSFrev6OY7jngzFk9OT/fn73fWYpqnlXNyXDMWT0zuYx/Bvel9ej+LJ6R08DMOu67q7DkTkrSA5vYPneV71fX/QASdTkJwezhs0TfMARn0wMDDGXEPgF4oijqwM5YjNAAAAAElFTkSuQmCC);
|
||||
}
|
||||
|
||||
/* white unsorted icon; updated to use bootstrap-icon-white - see #1432 */
|
||||
.tablesorter-bootstrap .bootstrap-icon-white.bootstrap-icon-unsorted {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOCAYAAAD5YeaVAAAAe0lEQVR4AbXQoRWDMBiF0Sh2QLAAQ8SxJGugWSA6A2STW1PxTsnB9cnkfuYvv8OGC1t5G3Y0QMP+Bm857keAdQIzWBP3+Bw4MADQE18B6/etRnCV/w9nnGuLezfAmXhABGtAGIkruvk6auIFRwQJDywllsEAjCecB20GP59BQQ+gtlRLAAAAAElFTkSuQmCC);
|
||||
}
|
||||
|
||||
/* since bootstrap (table-striped) uses nth-child(), we just use this to add a zebra stripe color */
|
||||
.tablesorter-bootstrap > tbody > tr.odd > td,
|
||||
.tablesorter-bootstrap > tbody > tr.tablesorter-hasChildRow.odd:hover ~ tr.tablesorter-hasChildRow.odd ~ .tablesorter-childRow.odd > td {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
.tablesorter-bootstrap > tbody > tr.hover > td,
|
||||
.tablesorter-bootstrap > tbody > tr.odd:hover > td,
|
||||
.tablesorter-bootstrap > tbody > tr.even:hover > td,
|
||||
.tablesorter-bootstrap > tbody > tr.tablesorter-hasChildRow.odd:hover ~ .tablesorter-childRow.odd > td,
|
||||
.tablesorter-bootstrap > tbody > tr.tablesorter-hasChildRow.even:hover ~ .tablesorter-childRow.even > td {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.tablesorter-bootstrap > tbody > tr.even > td,
|
||||
.tablesorter-bootstrap > tbody > tr.tablesorter-hasChildRow.even:hover ~ tr.tablesorter-hasChildRow.even ~ .tablesorter-childRow.even > td {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/* processing icon */
|
||||
.tablesorter-bootstrap .tablesorter-processing {
|
||||
background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=');
|
||||
background-position: center center !important;
|
||||
background-repeat: no-repeat !important;
|
||||
}
|
||||
|
||||
/* Column Widget - column sort colors */
|
||||
.tablesorter-bootstrap > tbody > tr.odd td.primary {
|
||||
background-color: #bfbfbf;
|
||||
}
|
||||
.tablesorter-bootstrap > tbody > tr td.primary,
|
||||
.tablesorter-bootstrap > tbody > tr.even td.primary {
|
||||
background-color: #d9d9d9;
|
||||
}
|
||||
.tablesorter-bootstrap > tbody > tr.odd td.secondary {
|
||||
background-color: #d9d9d9;
|
||||
}
|
||||
.tablesorter-bootstrap > tbody > tr td.secondary,
|
||||
.tablesorter-bootstrap > tbody > tr.even td.secondary {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
.tablesorter-bootstrap > tbody > tr.odd td.tertiary {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
.tablesorter-bootstrap > tbody > tr td.tertiary,
|
||||
.tablesorter-bootstrap > tbody > tr.even td.tertiary {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
/* caption */
|
||||
.tablesorter-bootstrap > .caption {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/* filter widget */
|
||||
.tablesorter-bootstrap .tablesorter-filter-row input.tablesorter-filter,
|
||||
.tablesorter-bootstrap .tablesorter-filter-row select.tablesorter-filter {
|
||||
width: 98%;
|
||||
margin: 0;
|
||||
padding: 4px 6px;
|
||||
color: #333;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-transition: height 0.1s ease;
|
||||
-moz-transition: height 0.1s ease;
|
||||
-o-transition: height 0.1s ease;
|
||||
transition: height 0.1s ease;
|
||||
}
|
||||
.tablesorter-bootstrap .tablesorter-filter-row .tablesorter-filter.disabled {
|
||||
background-color: #eee;
|
||||
color: #555;
|
||||
cursor: not-allowed;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.075) inset;
|
||||
box-sizing: border-box;
|
||||
transition: height 0.1s ease;
|
||||
}
|
||||
.tablesorter-bootstrap .tablesorter-filter-row {
|
||||
background-color: #efefef;
|
||||
}
|
||||
.tablesorter-bootstrap .tablesorter-filter-row td {
|
||||
background-color: #efefef;
|
||||
line-height: normal;
|
||||
text-align: center;
|
||||
padding: 4px 6px;
|
||||
vertical-align: middle;
|
||||
-webkit-transition: line-height 0.1s ease;
|
||||
-moz-transition: line-height 0.1s ease;
|
||||
-o-transition: line-height 0.1s ease;
|
||||
transition: line-height 0.1s ease;
|
||||
}
|
||||
/* hidden filter row */
|
||||
.tablesorter-bootstrap .tablesorter-filter-row.hideme td {
|
||||
padding: 2px; /* change this to modify the thickness of the closed border row */
|
||||
margin: 0;
|
||||
line-height: 0;
|
||||
}
|
||||
.tablesorter-bootstrap .tablesorter-filter-row.hideme * {
|
||||
height: 1px;
|
||||
min-height: 0;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
/* don't use visibility: hidden because it disables tabbing */
|
||||
opacity: 0;
|
||||
filter: alpha(opacity=0);
|
||||
}
|
||||
/* rows hidden by filtering */
|
||||
.tablesorter .filtered {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* pager plugin */
|
||||
.tablesorter-bootstrap .tablesorter-pager select {
|
||||
padding: 4px 6px;
|
||||
}
|
||||
.tablesorter-bootstrap .tablesorter-pager .pagedisplay {
|
||||
border: 0;
|
||||
}
|
||||
/* tfoot i for pager controls */
|
||||
.tablesorter-bootstrap tfoot i {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
/* ajax error row */
|
||||
.tablesorter .tablesorter-errorRow td {
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
background-color: #e6bf99;
|
||||
}
|
||||
BIN
src/main/resources/assets/images/favicon-32x32.png
Normal file
BIN
src/main/resources/assets/images/favicon-32x32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
BIN
src/main/resources/assets/images/favicon.ico
Normal file
BIN
src/main/resources/assets/images/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
BIN
src/main/resources/assets/images/logo.png
Normal file
BIN
src/main/resources/assets/images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
7
src/main/resources/assets/js/bootstrap.bundle.min.js
vendored
Normal file
7
src/main/resources/assets/js/bootstrap.bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
src/main/resources/assets/js/bootstrap.bundle.min.js.map
Normal file
1
src/main/resources/assets/js/bootstrap.bundle.min.js.map
Normal file
File diff suppressed because one or more lines are too long
207
src/main/resources/assets/js/fleet.js
Normal file
207
src/main/resources/assets/js/fleet.js
Normal file
@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Copyright (c) 2019 LinuxServer.io
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var ajaxManager = (function($) {
|
||||
|
||||
var handleError = function(jqXHR, textStatus, handleError) {
|
||||
|
||||
var error = JSON.parse(jqXHR.responseText);
|
||||
console.log(error);
|
||||
};
|
||||
|
||||
var call = function(param, onDone) {
|
||||
return $.ajax(param).done(onDone).fail(handleError);
|
||||
};
|
||||
|
||||
return {
|
||||
call: call
|
||||
};
|
||||
|
||||
}(jQuery));
|
||||
|
||||
var repositoryManager = (function($) {
|
||||
|
||||
var updateSyncSettings = function() {
|
||||
|
||||
var syncSwitch = $(this);
|
||||
var row = syncSwitch.parents('tr');
|
||||
var repositoryId = row.data('repository-id');
|
||||
var syncEnabled = syncSwitch.is(':checked');
|
||||
|
||||
var request = {
|
||||
url: '/admin/manageRepository',
|
||||
method: 'POST',
|
||||
data: {
|
||||
repositoryId: repositoryId,
|
||||
action: syncEnabled ? 'ENABLE_SYNC' : 'DISABLE_SYNC'
|
||||
}
|
||||
};
|
||||
|
||||
ajaxManager.call(request, function(data) { console.log("Updated") });
|
||||
};
|
||||
|
||||
var updateVersionMask = function() {
|
||||
|
||||
var versionMask = $(this);
|
||||
var row = versionMask.parents('tr');
|
||||
var repositoryId = row.data('repository-id');
|
||||
var maskValue = versionMask.val();
|
||||
|
||||
var request = {
|
||||
url: '/admin/manageRepository',
|
||||
method: 'POST',
|
||||
data: {
|
||||
repositoryId: repositoryId,
|
||||
action: 'MASK',
|
||||
versionMask: maskValue
|
||||
}
|
||||
};
|
||||
|
||||
ajaxManager.call(request, function(data) { console.log("Updated") });
|
||||
};
|
||||
|
||||
var init = function() {
|
||||
|
||||
$('.sync-repository').on('change', updateSyncSettings);
|
||||
$('.version-mask').on('blur', updateVersionMask);
|
||||
};
|
||||
|
||||
return {
|
||||
init: init
|
||||
};
|
||||
|
||||
}(jQuery));
|
||||
|
||||
var imageListManager = (function($) {
|
||||
|
||||
var showImage = function() {
|
||||
|
||||
var option = $(this);
|
||||
var row = getImageRow(option);
|
||||
var imageId = getImageId(row);
|
||||
var request = buildRequest('SHOW', imageId)
|
||||
|
||||
ajaxManager.call(request, function(data) {
|
||||
|
||||
row.removeClass('hidden-image');
|
||||
option.replaceWith(createHideButton());
|
||||
});
|
||||
};
|
||||
|
||||
var hideImage = function() {
|
||||
|
||||
var option = $(this);
|
||||
var row = getImageRow(option);
|
||||
var imageId = getImageId(row);
|
||||
var request = buildRequest('HIDE', imageId)
|
||||
|
||||
var call = ajaxManager.call(request, function(data) {
|
||||
|
||||
row.addClass('hidden-image');
|
||||
option.replaceWith(createShowButton());
|
||||
});
|
||||
};
|
||||
|
||||
var markImageUnstable = function() {
|
||||
|
||||
var option = $(this);
|
||||
var row = getImageRow(option);
|
||||
var imageId = getImageId(row);
|
||||
var request = buildRequest('UNSTABLE', imageId)
|
||||
|
||||
var call = ajaxManager.call(request, function(data) {
|
||||
|
||||
row.find('.image-status').find('i.fas').replaceWith(createUnstableIcon());
|
||||
option.replaceWith(createStableButton());
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
var markImageStable = function() {
|
||||
|
||||
var option = $(this);
|
||||
var row = getImageRow(option);
|
||||
var imageId = getImageId(row);
|
||||
var request = buildRequest('STABLE', imageId)
|
||||
|
||||
var call = ajaxManager.call(request, function(data) {
|
||||
|
||||
row.find('.image-status').find('i.fas').replaceWith(createStableIcon());
|
||||
option.replaceWith(createUnstableButton());
|
||||
});
|
||||
};
|
||||
|
||||
var buildRequest = function(action, imageId) {
|
||||
|
||||
return {
|
||||
url: '/admin/manageImage',
|
||||
method: 'POST',
|
||||
data: {
|
||||
action: action,
|
||||
imageId: imageId
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var createHideButton = function() {
|
||||
return $('<button type="button" class="image--hide dropdown-item btn-clickable">Hide from list</button>');
|
||||
};
|
||||
|
||||
var createShowButton = function() {
|
||||
return $('<button type="button" class="image--show dropdown-item btn-clickable">Show in list</button>');
|
||||
};
|
||||
|
||||
var createStableButton = function() {
|
||||
return $('<button type="button" class="image--mark-stable dropdown-item btn-clickable">Mark as stable</button>');
|
||||
};
|
||||
|
||||
var createUnstableButton = function() {
|
||||
return $('<button type="button" class="image--mark-unstable dropdown-item btn-clickable">Mark as unstable</button>');
|
||||
};
|
||||
|
||||
var createUnstableIcon = function() {
|
||||
return $('<i class="fas fa-exclamation-triangle text-warning" title="Potentially unstable"></i>');
|
||||
};
|
||||
|
||||
var createStableIcon = function() {
|
||||
return $('<i class="fas fa-check-circle text-success" title="No issues reported"></i>');
|
||||
};
|
||||
|
||||
var getImageRow = function(item) {
|
||||
return item.parents('tr');
|
||||
};
|
||||
|
||||
var getImageId = function(row) {
|
||||
return parseInt(row.data('image-id'));
|
||||
};
|
||||
|
||||
var init = function() {
|
||||
|
||||
$('.admin-actions').on('click', '.image--show', showImage);
|
||||
$('.admin-actions').on('click', '.image--hide', hideImage);
|
||||
$('.admin-actions').on('click', '.image--mark-stable', markImageStable);
|
||||
$('.admin-actions').on('click', '.image--mark-unstable', markImageUnstable);
|
||||
};
|
||||
|
||||
return {
|
||||
init: init
|
||||
}
|
||||
|
||||
}(jQuery));
|
||||
|
||||
imageListManager.init();
|
||||
repositoryManager.init();
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user