mirror of
https://github.com/linuxserver/fleet.git
synced 2026-02-20 05:11:08 +08:00
commit
1ac46d3380
@ -11,7 +11,7 @@ repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
version = '1.2.1'
|
||||
version = '1.3.0'
|
||||
|
||||
sourceSets {
|
||||
|
||||
|
||||
@ -130,7 +130,8 @@ public class FleetApp {
|
||||
*/
|
||||
path("/api/v1", () -> {
|
||||
|
||||
get("/images", new AllImagesApi(beans.getRepositoryDelegate(), beans.getImageDelegate()), new JsonTransformer());
|
||||
get("/images", new AllImagesApi(beans.getRepositoryDelegate(), beans.getImageDelegate()), new JsonTransformer());
|
||||
get("/pullHistory", new GetImagePullHistoryApi(beans.getImageDelegate()), new JsonTransformer());
|
||||
|
||||
after("/*", (request, response) -> {
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@ 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 io.linuxserver.fleet.model.ImagePullStat;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -29,9 +30,7 @@ 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;
|
||||
import static io.linuxserver.fleet.db.dao.Utils.setNullableString;
|
||||
import static io.linuxserver.fleet.db.dao.Utils.*;
|
||||
|
||||
public class DefaultImageDAO implements ImageDAO {
|
||||
|
||||
@ -46,9 +45,11 @@ public class DefaultImageDAO implements ImageDAO {
|
||||
@Override
|
||||
public Image findImageByRepositoryAndImageName(int repositoryId, String imageName) {
|
||||
|
||||
CallableStatement call = null;
|
||||
|
||||
try (Connection connection = databaseConnection.getConnection()) {
|
||||
|
||||
CallableStatement call = connection.prepareCall("{CALL Image_GetByName(?,?)}");
|
||||
call = connection.prepareCall("{CALL Image_GetByName(?,?)}");
|
||||
call.setInt(1, repositoryId);
|
||||
call.setString(2, imageName);
|
||||
|
||||
@ -58,6 +59,8 @@ public class DefaultImageDAO implements ImageDAO {
|
||||
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Unable to fetch image", e);
|
||||
} finally {
|
||||
safeClose(call);
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -68,9 +71,11 @@ public class DefaultImageDAO implements ImageDAO {
|
||||
|
||||
LOGGER.debug("Fetching image by ID: " + id);
|
||||
|
||||
CallableStatement call = null;
|
||||
|
||||
try (Connection connection = databaseConnection.getConnection()) {
|
||||
|
||||
CallableStatement call = connection.prepareCall("{CALL Image_Get(?)}");
|
||||
call = connection.prepareCall("{CALL Image_Get(?)}");
|
||||
call.setInt(1, id);
|
||||
|
||||
ResultSet results = call.executeQuery();
|
||||
@ -79,6 +84,8 @@ public class DefaultImageDAO implements ImageDAO {
|
||||
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Unable to fetch image.", e);
|
||||
} finally {
|
||||
safeClose(call);
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -89,9 +96,11 @@ public class DefaultImageDAO implements ImageDAO {
|
||||
|
||||
List<Image> images = new ArrayList<>();
|
||||
|
||||
CallableStatement call = null;
|
||||
|
||||
try (Connection connection = databaseConnection.getConnection()) {
|
||||
|
||||
CallableStatement call = connection.prepareCall("{CALL Image_GetAll(?,?)}");
|
||||
call = connection.prepareCall("{CALL Image_GetAll(?,?)}");
|
||||
call.setInt(1, repositoryId);
|
||||
call.registerOutParameter(2, Types.INTEGER);
|
||||
|
||||
@ -104,6 +113,8 @@ public class DefaultImageDAO implements ImageDAO {
|
||||
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Unable to get all images", e);
|
||||
} finally {
|
||||
safeClose(call);
|
||||
}
|
||||
|
||||
return new LimitedResult<>(images, images.size());
|
||||
@ -112,9 +123,11 @@ public class DefaultImageDAO implements ImageDAO {
|
||||
@Override
|
||||
public InsertUpdateResult<Image> saveImage(Image image) {
|
||||
|
||||
CallableStatement call = null;
|
||||
|
||||
try (Connection connection = databaseConnection.getConnection()) {
|
||||
|
||||
CallableStatement call = connection.prepareCall("{CALL Image_Save(?,?,?,?,?,?,?,?,?,?,?,?,?)");
|
||||
call = connection.prepareCall("{CALL Image_Save(?,?,?,?,?,?,?,?,?,?,?,?,?)");
|
||||
setNullableInt(call, 1, image.getId());
|
||||
call.setInt(2, image.getRepositoryId());
|
||||
call.setString(3, image.getName());
|
||||
@ -145,24 +158,70 @@ public class DefaultImageDAO implements ImageDAO {
|
||||
|
||||
LOGGER.error("Unable to save image", e);
|
||||
return new InsertUpdateResult<>(null, InsertUpdateStatus.OK, "Unable to save image");
|
||||
|
||||
} finally {
|
||||
safeClose(call);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeImage(Integer id) {
|
||||
|
||||
CallableStatement call = null;
|
||||
|
||||
try (Connection connection = databaseConnection.getConnection()) {
|
||||
|
||||
PreparedStatement call = connection.prepareStatement("DELETE FROM Images WHERE `id` = ?");
|
||||
call = connection.prepareCall("{CALL Image_Delete(?)}");
|
||||
call.setInt(1, id);
|
||||
|
||||
call.executeUpdate();
|
||||
call.close();
|
||||
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Error when removing image", e);
|
||||
} finally {
|
||||
safeClose(call);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ImagePullStat> fetchImagePullHistory(Integer imageId, ImagePullStat.GroupMode groupMode) {
|
||||
|
||||
List<ImagePullStat> pullHistory = new ArrayList<>();
|
||||
|
||||
CallableStatement call = null;
|
||||
|
||||
try (Connection connection = databaseConnection.getConnection()) {
|
||||
|
||||
call = connection.prepareCall("CALL Image_GetPullHistory(?, ?)");
|
||||
call.setInt(1, imageId);
|
||||
call.setString(2, groupMode.toString());
|
||||
|
||||
ResultSet results = call.executeQuery();
|
||||
while (results.next())
|
||||
pullHistory.add(parseImagePullHistoryFromResultSet(results, groupMode));
|
||||
|
||||
call.close();
|
||||
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Error when fetching image pull history", e);
|
||||
} finally {
|
||||
safeClose(call);
|
||||
}
|
||||
|
||||
return pullHistory;
|
||||
}
|
||||
|
||||
private ImagePullStat parseImagePullHistoryFromResultSet(ResultSet results, ImagePullStat.GroupMode groupMode) throws SQLException {
|
||||
|
||||
return new ImagePullStat(
|
||||
results.getInt("ImageId"),
|
||||
results.getString("TimeGroup"),
|
||||
results.getLong("ImagePulls"),
|
||||
groupMode
|
||||
);
|
||||
}
|
||||
|
||||
private Image parseImageFromResultSet(ResultSet results) throws SQLException {
|
||||
|
||||
Image image = new Image(
|
||||
|
||||
@ -28,6 +28,7 @@ import java.sql.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static io.linuxserver.fleet.db.dao.Utils.safeClose;
|
||||
import static io.linuxserver.fleet.db.dao.Utils.setNullableInt;
|
||||
import static io.linuxserver.fleet.db.dao.Utils.setNullableString;
|
||||
|
||||
@ -44,9 +45,11 @@ public class DefaultRepositoryDAO implements RepositoryDAO {
|
||||
@Override
|
||||
public Repository fetchRepository(int id) {
|
||||
|
||||
CallableStatement call = null;
|
||||
|
||||
try (Connection connection = databaseConnection.getConnection()) {
|
||||
|
||||
CallableStatement call = connection.prepareCall("{CALL Repository_Get(?)}");
|
||||
call = connection.prepareCall("{CALL Repository_Get(?)}");
|
||||
call.setInt(1, id);
|
||||
|
||||
ResultSet results = call.executeQuery();
|
||||
@ -55,6 +58,8 @@ public class DefaultRepositoryDAO implements RepositoryDAO {
|
||||
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Unable to retrieve repository", e);
|
||||
} finally {
|
||||
safeClose(call);
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -63,9 +68,11 @@ public class DefaultRepositoryDAO implements RepositoryDAO {
|
||||
@Override
|
||||
public InsertUpdateResult<Repository> saveRepository(Repository repository) {
|
||||
|
||||
CallableStatement call = null;
|
||||
|
||||
try (Connection connection = databaseConnection.getConnection()) {
|
||||
|
||||
CallableStatement call = connection.prepareCall("{CALL Repository_Save(?,?,?,?,?,?,?)}");
|
||||
call = connection.prepareCall("{CALL Repository_Save(?,?,?,?,?,?,?)}");
|
||||
setNullableInt(call, 1, repository.getId());
|
||||
call.setString(2, repository.getName());
|
||||
setNullableString(call, 3, repository.getVersionMask());
|
||||
@ -90,6 +97,9 @@ public class DefaultRepositoryDAO implements RepositoryDAO {
|
||||
|
||||
LOGGER.error("Unable to save repository", e);
|
||||
return new InsertUpdateResult<>(null, InsertUpdateStatus.OK, "Unable to save repository");
|
||||
|
||||
} finally {
|
||||
safeClose(call);
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,9 +108,11 @@ public class DefaultRepositoryDAO implements RepositoryDAO {
|
||||
|
||||
List<Repository> repositories = new ArrayList<>();
|
||||
|
||||
CallableStatement call = null;
|
||||
|
||||
try (Connection connection = databaseConnection.getConnection()) {
|
||||
|
||||
CallableStatement call = connection.prepareCall("{CALL Repository_GetAll()}");
|
||||
call = connection.prepareCall("{CALL Repository_GetAll()}");
|
||||
|
||||
ResultSet results = call.executeQuery();
|
||||
while (results.next())
|
||||
@ -108,6 +120,8 @@ public class DefaultRepositoryDAO implements RepositoryDAO {
|
||||
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Unable to get all repositories", e);
|
||||
} finally {
|
||||
safeClose(call);
|
||||
}
|
||||
|
||||
return repositories;
|
||||
@ -116,9 +130,11 @@ public class DefaultRepositoryDAO implements RepositoryDAO {
|
||||
@Override
|
||||
public Repository findRepositoryByName(String name) {
|
||||
|
||||
CallableStatement call = null;
|
||||
|
||||
try (Connection connection = databaseConnection.getConnection()) {
|
||||
|
||||
CallableStatement call = connection.prepareCall("{CALL Repository_GetByName(?)}");
|
||||
call = connection.prepareCall("{CALL Repository_GetByName(?)}");
|
||||
call.setString(1, name);
|
||||
|
||||
ResultSet results = call.executeQuery();
|
||||
@ -127,6 +143,8 @@ public class DefaultRepositoryDAO implements RepositoryDAO {
|
||||
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Unable to retrieve repository", e);
|
||||
} finally {
|
||||
safeClose(call);
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -135,15 +153,19 @@ public class DefaultRepositoryDAO implements RepositoryDAO {
|
||||
@Override
|
||||
public void removeRepository(int id) {
|
||||
|
||||
CallableStatement call = null;
|
||||
|
||||
try (Connection connection = databaseConnection.getConnection()) {
|
||||
|
||||
CallableStatement call = connection.prepareCall("{CALL Repository_Delete(?)}");
|
||||
call = connection.prepareCall("{CALL Repository_Delete(?)}");
|
||||
call.setInt(1, id);
|
||||
|
||||
call.executeUpdate();
|
||||
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Error when removing repository", e);
|
||||
} finally {
|
||||
safeClose(call);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,6 +3,9 @@ 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;
|
||||
import io.linuxserver.fleet.model.ImagePullStat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ImageDAO {
|
||||
|
||||
@ -15,4 +18,6 @@ public interface ImageDAO {
|
||||
InsertUpdateResult<Image> saveImage(Image image);
|
||||
|
||||
void removeImage(Integer id);
|
||||
|
||||
List<ImagePullStat> fetchImagePullHistory(Integer imageId, ImagePullStat.GroupMode groupMode);
|
||||
}
|
||||
|
||||
@ -49,4 +49,16 @@ class Utils {
|
||||
else
|
||||
call.setString(position, value);
|
||||
}
|
||||
|
||||
static void safeClose(CallableStatement call) {
|
||||
|
||||
try {
|
||||
|
||||
if (null != call)
|
||||
call.close();
|
||||
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException("Unable to close call", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,6 +22,7 @@ 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 io.linuxserver.fleet.model.ImagePullStat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -65,4 +66,8 @@ public class ImageDelegate {
|
||||
|
||||
throw new SaveException(result.getStatusMessage());
|
||||
}
|
||||
|
||||
public List<ImagePullStat> fetchImagePullHistory(int id, ImagePullStat.GroupMode groupMode) {
|
||||
return imageDAO.fetchImagePullHistory(id, groupMode);
|
||||
}
|
||||
}
|
||||
|
||||
76
src/main/java/io/linuxserver/fleet/model/ImagePullStat.java
Normal file
76
src/main/java/io/linuxserver/fleet/model/ImagePullStat.java
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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 ImagePullStat {
|
||||
|
||||
private final int imageId;
|
||||
private final String timeGroup;
|
||||
private final long pullCount;
|
||||
private final GroupMode groupMode;
|
||||
|
||||
public ImagePullStat(int imageId, String timeGroup, long pullCount, GroupMode groupMode) {
|
||||
|
||||
this.imageId = imageId;
|
||||
this.timeGroup = timeGroup;
|
||||
this.pullCount = pullCount;
|
||||
this.groupMode = groupMode;
|
||||
}
|
||||
|
||||
public int getImageId() {
|
||||
return imageId;
|
||||
}
|
||||
|
||||
public String getTimeGroup() {
|
||||
return timeGroup;
|
||||
}
|
||||
|
||||
public long getPullCount() {
|
||||
return pullCount;
|
||||
}
|
||||
|
||||
public GroupMode getGroupMode() {
|
||||
return groupMode;
|
||||
}
|
||||
|
||||
public enum GroupMode {
|
||||
|
||||
HOUR, DAY, WEEK, MONTH, YEAR;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name().toLowerCase();
|
||||
}
|
||||
|
||||
public static boolean isValid(String value) {
|
||||
|
||||
try {
|
||||
|
||||
if (null == value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
valueOf(value);
|
||||
return true;
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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;
|
||||
import io.linuxserver.fleet.model.ImagePullStat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ApiImagePullHistory {
|
||||
|
||||
private int imageId;
|
||||
private String imageName;
|
||||
private String groupMode;
|
||||
private List<ApiImagePullStat> pullHistory = new ArrayList<>();
|
||||
|
||||
public int getImageId() {
|
||||
return imageId;
|
||||
}
|
||||
|
||||
public String getImageName() {
|
||||
return imageName;
|
||||
}
|
||||
|
||||
public String getGroupMode() {
|
||||
return groupMode;
|
||||
}
|
||||
|
||||
public List<ApiImagePullStat> getPullHistory() {
|
||||
return pullHistory;
|
||||
}
|
||||
|
||||
public static ApiImagePullHistory fromPullStats(Image image, List<ImagePullStat> stats) {
|
||||
|
||||
ApiImagePullHistory history = new ApiImagePullHistory();
|
||||
history.imageId = image.getId();
|
||||
history.imageName = image.getName();
|
||||
history.groupMode = stats.get(0).getGroupMode().toString();
|
||||
|
||||
for (ImagePullStat stat : stats)
|
||||
history.pullHistory.add(new ApiImagePullStat(stat.getTimeGroup(), stat.getPullCount()));
|
||||
|
||||
return history;
|
||||
}
|
||||
|
||||
public static class ApiImagePullStat {
|
||||
|
||||
private final String timeGroup;
|
||||
private final long pullCount;
|
||||
|
||||
ApiImagePullStat(String timeGroup, long pullCount) {
|
||||
|
||||
this.timeGroup = timeGroup;
|
||||
this.pullCount = pullCount;
|
||||
}
|
||||
|
||||
public String getTimeGroup() {
|
||||
return timeGroup;
|
||||
}
|
||||
|
||||
public long getPullCount() {
|
||||
return pullCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -31,6 +31,7 @@ import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class DefaultSynchronisationState implements SynchronisationState {
|
||||
|
||||
@ -86,7 +87,9 @@ public class DefaultSynchronisationState implements SynchronisationState {
|
||||
try {
|
||||
|
||||
List<String> repositories = context.getDockerHubDelegate().fetchAllRepositories();
|
||||
|
||||
onRepositoryScanned(context, repositories);
|
||||
checkAndRemoveMissingRepositories(repositories, context);
|
||||
|
||||
for (String repositoryName : repositories)
|
||||
synchroniseRepository(repositoryName, context);
|
||||
@ -122,6 +125,7 @@ public class DefaultSynchronisationState implements SynchronisationState {
|
||||
if (repository.isSyncEnabled()) {
|
||||
|
||||
List<DockerHubImage> images = context.getDockerHubDelegate().fetchAllImagesFromRepository(repository.getName());
|
||||
checkAndRemoveMissingImages(repository, images, context);
|
||||
|
||||
int totalSize = images.size();
|
||||
LOGGER.info("Found {} images in Docker Hub", totalSize);
|
||||
@ -139,7 +143,7 @@ public class DefaultSynchronisationState implements SynchronisationState {
|
||||
image.withPullCount(dockerHubImage.getPullCount()).withVersion(maskedVersion);
|
||||
|
||||
context.getImageDelegate().saveImage(image);
|
||||
onImageUpdated(context, new ImageUpdateEvent(image, i, totalSize));
|
||||
onImageUpdated(context, new ImageUpdateEvent(image, i + 1, totalSize));
|
||||
|
||||
} catch (SaveException e) {
|
||||
LOGGER.error("Unable to save updated image", e);
|
||||
@ -151,6 +155,34 @@ public class DefaultSynchronisationState implements SynchronisationState {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAndRemoveMissingRepositories(List<String> repositories, SynchronisationContext context) {
|
||||
|
||||
LOGGER.info("Checking for any removed repositories.");
|
||||
for (Repository storedRepository : context.getRepositoryDelegate().fetchAllRepositories()) {
|
||||
|
||||
if (!repositories.contains(storedRepository.getName())) {
|
||||
|
||||
LOGGER.info("Found repository which no longer exists in Docker Hub. Removing {}", storedRepository.getName());
|
||||
context.getRepositoryDelegate().removeRepository(storedRepository.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAndRemoveMissingImages(Repository repository, List<DockerHubImage> images, SynchronisationContext context) {
|
||||
|
||||
List<String> dockerHubImageNames = images.stream().map(DockerHubImage::getName).collect(Collectors.toList());
|
||||
|
||||
LOGGER.info("Checking for any removed images under {}", repository.getName());
|
||||
for (Image storedImage : context.getImageDelegate().fetchImagesByRepository(repository.getId())) {
|
||||
|
||||
if (!dockerHubImageNames.contains(storedImage.getName())) {
|
||||
|
||||
LOGGER.info("Found image which no longer exists in Docker Hub. Removing {}", storedImage.getName());
|
||||
context.getImageDelegate().removeImage(storedImage.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getVersionMask(String repositoryMask, String imageMask) {
|
||||
return imageMask == null ? repositoryMask : imageMask;
|
||||
}
|
||||
|
||||
@ -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.routes;
|
||||
|
||||
import io.linuxserver.fleet.delegate.ImageDelegate;
|
||||
import io.linuxserver.fleet.model.Image;
|
||||
import io.linuxserver.fleet.model.ImagePullStat;
|
||||
import io.linuxserver.fleet.model.api.ApiImagePullHistory;
|
||||
import io.linuxserver.fleet.model.api.ApiResponse;
|
||||
import io.linuxserver.fleet.model.api.FleetApiException;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
import spark.Route;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GetImagePullHistoryApi implements Route {
|
||||
|
||||
private final ImageDelegate imageDelegate;
|
||||
|
||||
public GetImagePullHistoryApi(ImageDelegate imageDelegate) {
|
||||
this.imageDelegate = imageDelegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handle(Request request, Response response) {
|
||||
|
||||
String imageIdParam = request.queryParams("imageId");
|
||||
if (null == imageIdParam) {
|
||||
throw new FleetApiException(400, "Missing imageId param");
|
||||
}
|
||||
|
||||
int imageId = Integer.parseInt(imageIdParam);
|
||||
List<ImagePullStat> imagePullStats = imageDelegate.fetchImagePullHistory(imageId, getGroupMode(request));
|
||||
|
||||
Image image = imageDelegate.fetchImage(imageId);
|
||||
return new ApiResponse<>("OK", ApiImagePullHistory.fromPullStats(image, imagePullStats));
|
||||
}
|
||||
|
||||
private ImagePullStat.GroupMode getGroupMode(Request request) {
|
||||
|
||||
String groupMode = request.queryParams("groupMode");
|
||||
return ImagePullStat.GroupMode.isValid(groupMode) ? ImagePullStat.GroupMode.valueOf(groupMode) : ImagePullStat.GroupMode.DAY;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,220 @@
|
||||
DELIMITER //
|
||||
|
||||
CREATE TABLE ImagePullHistory (
|
||||
`image_id` INT NOT NULL,
|
||||
`pull_timestamp` BIGINT NOT NULL,
|
||||
`pull_count` BIGINT NOT NULL,
|
||||
PRIMARY KEY (`image_id`, `pull_timestamp`)
|
||||
) ENGINE=InnoDB;
|
||||
//
|
||||
|
||||
CREATE PROCEDURE `Image_SavePullHistory`
|
||||
(
|
||||
in_image_id INT,
|
||||
in_image_pulls BIGINT,
|
||||
|
||||
OUT out_status INT,
|
||||
OUT out_message VARCHAR(100)
|
||||
)
|
||||
BEGIN
|
||||
|
||||
IF EXISTS(SELECT 1 FROM Images WHERE `id` = in_image_id) THEN
|
||||
|
||||
INSERT INTO ImagePullHistory
|
||||
(
|
||||
`image_id`,
|
||||
`pull_timestamp`,
|
||||
`pull_count`
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
in_image_id,
|
||||
UNIX_TIMESTAMP(NOW()),
|
||||
in_image_pulls
|
||||
);
|
||||
|
||||
END IF;
|
||||
|
||||
SET out_status = 0;
|
||||
SET out_message = "OK";
|
||||
|
||||
END;
|
||||
//
|
||||
|
||||
CREATE PROCEDURE `Image_GetPullHistory`
|
||||
(
|
||||
in_image_id INT,
|
||||
in_grouping_mode ENUM('hour', 'day', 'week', 'month', 'year')
|
||||
)
|
||||
BEGIN
|
||||
|
||||
IF in_grouping_mode = 'hour' THEN
|
||||
|
||||
SELECT
|
||||
`image_id` AS ImageId,
|
||||
MAX(`pull_count`) AS ImagePulls,
|
||||
FROM_UNIXTIME(`pull_timestamp`, '%Y%m%d%H') AS TimeGroup
|
||||
FROM
|
||||
ImagePullHistory
|
||||
WHERE
|
||||
`image_id` = in_image_id
|
||||
GROUP BY `image_id`, TimeGroup
|
||||
ORDER BY TimeGroup;
|
||||
|
||||
ELSEIF in_grouping_mode = 'day' THEN
|
||||
|
||||
SELECT
|
||||
`image_id` AS ImageId,
|
||||
MAX(`pull_count`) AS ImagePulls,
|
||||
FROM_UNIXTIME(`pull_timestamp`, '%Y%m%d') AS TimeGroup
|
||||
FROM
|
||||
ImagePullHistory
|
||||
WHERE
|
||||
`image_id` = in_image_id
|
||||
GROUP BY `image_id`, TimeGroup
|
||||
ORDER BY TimeGroup;
|
||||
|
||||
ELSEIF in_grouping_mode = 'week' THEN
|
||||
|
||||
SELECT
|
||||
`image_id` AS ImageId,
|
||||
MAX(`pull_count`) AS ImagePulls,
|
||||
FROM_UNIXTIME(`pull_timestamp`, '%Y%v') AS TimeGroup
|
||||
FROM
|
||||
ImagePullHistory
|
||||
WHERE
|
||||
`image_id` = in_image_id
|
||||
GROUP BY `image_id`, TimeGroup
|
||||
ORDER BY TimeGroup;
|
||||
|
||||
ELSEIF in_grouping_mode = 'month' THEN
|
||||
|
||||
SELECT
|
||||
`image_id` AS ImageId,
|
||||
MAX(`pull_count`) AS ImagePulls,
|
||||
FROM_UNIXTIME(`pull_timestamp`, '%Y%m') AS TimeGroup
|
||||
FROM
|
||||
ImagePullHistory
|
||||
WHERE
|
||||
`image_id` = in_image_id
|
||||
GROUP BY `image_id`, TimeGroup
|
||||
ORDER BY TimeGroup;
|
||||
|
||||
ELSEIF in_grouping_mode = 'year' THEN
|
||||
|
||||
SELECT
|
||||
`image_id` AS ImageId,
|
||||
MAX(`pull_count`) AS ImagePulls,
|
||||
FROM_UNIXTIME(`pull_timestamp`, '%Y') AS TimeGroup
|
||||
FROM
|
||||
ImagePullHistory
|
||||
WHERE
|
||||
`image_id` = in_image_id
|
||||
GROUP BY `image_id`, TimeGroup
|
||||
ORDER BY TimeGroup;
|
||||
|
||||
END IF;
|
||||
|
||||
END;
|
||||
//
|
||||
|
||||
DROP PROCEDURE `Image_Save`//
|
||||
CREATE PROCEDURE `Image_Save`
|
||||
(
|
||||
in_id INT,
|
||||
in_repository INT,
|
||||
in_name VARCHAR(255),
|
||||
in_pull_count BIGINT,
|
||||
in_version VARCHAR(100),
|
||||
in_version_mask VARCHAR(255),
|
||||
in_hidden TINYINT,
|
||||
in_unstable TINYINT,
|
||||
in_deprecated TINYINT,
|
||||
in_deprecation_reason VARCHAR(255),
|
||||
|
||||
OUT out_id INT,
|
||||
OUT out_status INT,
|
||||
OUT out_message VARCHAR(100)
|
||||
)
|
||||
BEGIN
|
||||
|
||||
IF in_id IS NULL THEN
|
||||
|
||||
INSERT INTO Images
|
||||
(
|
||||
`repository`,
|
||||
`name`,
|
||||
`pulls`,
|
||||
`latest_version`,
|
||||
`version_mask`,
|
||||
`hidden`,
|
||||
`unstable`,
|
||||
`deprecated`,
|
||||
`deprecation_reason`
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
in_repository,
|
||||
in_name,
|
||||
in_pull_count,
|
||||
in_version,
|
||||
in_version_mask,
|
||||
in_hidden,
|
||||
in_unstable,
|
||||
in_deprecated,
|
||||
in_deprecation_reason
|
||||
);
|
||||
|
||||
SET out_id = LAST_INSERT_ID();
|
||||
SET out_status = 0;
|
||||
SET out_message = 'OK';
|
||||
|
||||
ELSE
|
||||
|
||||
UPDATE Images
|
||||
SET
|
||||
`name` = in_name,
|
||||
`pulls` = in_pull_count,
|
||||
`latest_version` = in_version,
|
||||
`version_mask` = in_version_mask,
|
||||
`hidden` = in_hidden,
|
||||
`unstable` = in_unstable,
|
||||
`deprecated` = in_deprecated,
|
||||
`deprecation_reason` = in_deprecation_reason
|
||||
WHERE
|
||||
`id` = in_id;
|
||||
|
||||
SET out_id = in_id;
|
||||
|
||||
CALL Image_SavePullHistory(out_id, in_pull_count, out_status, out_message);
|
||||
|
||||
END IF;
|
||||
|
||||
END;
|
||||
//
|
||||
|
||||
DROP PROCEDURE `Repository_Delete`//
|
||||
CREATE PROCEDURE `Repository_Delete`
|
||||
(
|
||||
in_id INT
|
||||
)
|
||||
BEGIN
|
||||
|
||||
DELETE FROM ImagePullHistory WHERE `image_id` IN (SELECT `id` FROM Images WHERE `repository` = in_id);
|
||||
DELETE FROM Images WHERE `repository` = in_id;
|
||||
DELETE FROM Repositories WHERE `id` = in_id;
|
||||
|
||||
END;
|
||||
//
|
||||
|
||||
CREATE PROCEDURE `Image_Delete`
|
||||
(
|
||||
in_id INT
|
||||
)
|
||||
BEGIN
|
||||
|
||||
DELETE FROM ImagePullHistory WHERE `image_id` = in_id;
|
||||
DELETE FROM Images WHERE `id` = in_id;
|
||||
|
||||
END;
|
||||
//
|
||||
Loading…
x
Reference in New Issue
Block a user