mirror of
https://github.com/linuxserver/fleet.git
synced 2026-02-20 05:11:08 +08:00
Graphing/Edit Image
- Updated graph on view image page - Initial form format for edit image page.
This commit is contained in:
parent
cf5b277ff2
commit
4ccca5e55a
@ -161,4 +161,10 @@ public class FleetAppController extends AbstractAppController implements Service
|
||||
public final AuthenticationResult authenticateUser(final String username, final String password) {
|
||||
return authenticationDelegate.authenticate(username, password);
|
||||
}
|
||||
|
||||
public final void trackBranch(final ImageKey imageKey, final String branchName) {
|
||||
|
||||
getRepositoryService().trackBranchOnImage(imageKey, branchName);
|
||||
synchroniseImage(imageKey);
|
||||
}
|
||||
}
|
||||
|
||||
30
src/main/java/io/linuxserver/fleet/v2/Utils.java
Normal file
30
src/main/java/io/linuxserver/fleet/v2/Utils.java
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2020 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.v2;
|
||||
|
||||
public final class Utils {
|
||||
|
||||
public static <T> T ensureNotNull(final T obj) {
|
||||
|
||||
if (null == obj) {
|
||||
throw new IllegalArgumentException("Parameter null");
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
@ -33,6 +33,7 @@ import io.linuxserver.fleet.v2.types.meta.history.ImagePullHistory;
|
||||
import io.linuxserver.fleet.v2.types.meta.history.ImagePullStatistic;
|
||||
|
||||
import java.sql.*;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
@ -58,6 +59,7 @@ public class DefaultImageDAO extends AbstractDAO implements ImageDAO {
|
||||
private static final String GetImage = "{CALL Image_Get(?)}";
|
||||
private static final String DeleteImage = "{CALL Image_Delete(?)}";
|
||||
private static final String GetImageStats = "{CALL Image_GetStats(?)}";
|
||||
private static final String DeleteStats = "{CALL Image_ClearStatsBefore(?)}";
|
||||
|
||||
public DefaultImageDAO(final DatabaseProvider databaseConnection) {
|
||||
super(databaseConnection);
|
||||
|
||||
@ -27,6 +27,7 @@ import io.linuxserver.fleet.v2.types.internal.ImageOutlineRequest;
|
||||
import io.linuxserver.fleet.v2.types.internal.RepositoryOutlineRequest;
|
||||
import io.linuxserver.fleet.v2.types.internal.TagBranchOutlineRequest;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
public interface ImageDAO {
|
||||
|
||||
@ -17,12 +17,14 @@
|
||||
|
||||
package io.linuxserver.fleet.v2.key;
|
||||
|
||||
import io.linuxserver.fleet.v2.Utils;
|
||||
|
||||
public abstract class AbstractHasKey<KEY extends Key> implements HasKey<KEY> {
|
||||
|
||||
private final KEY key;
|
||||
|
||||
public AbstractHasKey(final KEY key) {
|
||||
this.key = key;
|
||||
this.key = Utils.ensureNotNull(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -29,6 +29,7 @@ import io.linuxserver.fleet.v2.types.docker.DockerImage;
|
||||
import io.linuxserver.fleet.v2.types.docker.DockerTag;
|
||||
import io.linuxserver.fleet.v2.types.internal.ImageOutlineRequest;
|
||||
import io.linuxserver.fleet.v2.types.internal.RepositoryOutlineRequest;
|
||||
import io.linuxserver.fleet.v2.types.internal.TagBranchOutlineRequest;
|
||||
import io.linuxserver.fleet.v2.types.meta.ItemSyncSpec;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -232,6 +233,27 @@ public class RepositoryService {
|
||||
}
|
||||
}
|
||||
|
||||
public void trackBranchOnImage(final ImageKey imageKey, final String branchName) {
|
||||
|
||||
final Image image = repositoryCache.findImage(imageKey);
|
||||
if (null == image) {
|
||||
throw new IllegalArgumentException("Could not find image with key " + imageKey);
|
||||
}
|
||||
|
||||
if (image.findTagBranchByName(branchName) != null) {
|
||||
throw new IllegalArgumentException("Image is already tracking branch " + branchName);
|
||||
}
|
||||
|
||||
final InsertUpdateResult<TagBranch> outlineResult = imageDAO.createTagBranchOutline(new TagBranchOutlineRequest(imageKey, branchName));
|
||||
if (outlineResult.isError()) {
|
||||
throw new RuntimeException(outlineResult.getStatusMessage());
|
||||
}
|
||||
|
||||
final Image updatableClone = image.cloneForUpdate();
|
||||
updatableClone.addTagBranch(outlineResult.getResult());
|
||||
storeImage(updatableClone);
|
||||
}
|
||||
|
||||
private void updateCache(final Image storedImage) {
|
||||
|
||||
final Repository imageParentRepository = repositoryCache.findItem(storedImage.getRepositoryKey());
|
||||
|
||||
@ -28,6 +28,6 @@ public class CheckAppVersionSchedule extends AbstractAppSchedule {
|
||||
|
||||
@Override
|
||||
public void executeSchedule() {
|
||||
|
||||
getLogger().info("Currently not implemented. This is a placeholder schedule");
|
||||
}
|
||||
}
|
||||
|
||||
@ -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.v2.thread.schedule;
|
||||
|
||||
import io.linuxserver.fleet.core.FleetAppController;
|
||||
|
||||
public class TidyHistoricDataSchedule extends AbstractAppSchedule {
|
||||
|
||||
public TidyHistoricDataSchedule(final ScheduleSpec spec,
|
||||
final FleetAppController controller) {
|
||||
super(spec, controller);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeSchedule() {
|
||||
getLogger().info("Currently not implemented. This is a placeholder schedule");
|
||||
}
|
||||
}
|
||||
@ -67,6 +67,10 @@ public class Image extends AbstractSyncItem<ImageKey, Image> {
|
||||
return cloned;
|
||||
}
|
||||
|
||||
public final Image cloneForUpdate() {
|
||||
return cloneWithSyncSpec(getSpec());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Image cloneWithSyncSpec(final ItemSyncSpec syncSpec) {
|
||||
|
||||
|
||||
@ -18,6 +18,8 @@
|
||||
package io.linuxserver.fleet.v2.types;
|
||||
|
||||
import io.linuxserver.fleet.v2.key.AbstractHasKey;
|
||||
import io.linuxserver.fleet.v2.key.HasKey;
|
||||
import io.linuxserver.fleet.v2.key.ImageKey;
|
||||
import io.linuxserver.fleet.v2.key.TagBranchKey;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@ -24,8 +24,16 @@ import java.util.stream.Collectors;
|
||||
|
||||
public class ApiImagePullHistoryWrapper extends AbstractApiWrapper<List<ImagePullStatistic>> {
|
||||
|
||||
public ApiImagePullHistoryWrapper(final List<ImagePullStatistic> originalObject) {
|
||||
private final ImagePullStatistic.StatGroupMode groupMode;
|
||||
|
||||
public ApiImagePullHistoryWrapper(final List<ImagePullStatistic> originalObject,
|
||||
final ImagePullStatistic.StatGroupMode groupMode) {
|
||||
super(originalObject);
|
||||
this.groupMode = groupMode;
|
||||
}
|
||||
|
||||
public final String getGroupModeDataPoint() {
|
||||
return groupMode.getDataPoint();
|
||||
}
|
||||
|
||||
public final List<String> getLabels() {
|
||||
@ -36,6 +44,10 @@ public class ApiImagePullHistoryWrapper extends AbstractApiWrapper<List<ImagePul
|
||||
return getOriginalObject().stream().map(ImagePullStatistic::getPullCount).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public final long getMean() {
|
||||
return (long) getPullDifferential().getPulls().stream().mapToLong(Long::longValue).average().orElse(0.0);
|
||||
}
|
||||
|
||||
public final PullDifferentialsWithLabels getPullDifferential() {
|
||||
|
||||
final PullDifferentialsWithLabels differentialsWithLabels = new PullDifferentialsWithLabels();
|
||||
|
||||
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2020 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.v2.types.docker;
|
||||
|
||||
public enum DockerCapability {
|
||||
|
||||
AUDIT_CONTROL,
|
||||
AUDIT_WRITE,
|
||||
BLOCK_SUSPEND,
|
||||
CHOWN,
|
||||
DAC_OVERRIDE,
|
||||
DAC_READ_SEARCH,
|
||||
FOWNER,
|
||||
FSETID,
|
||||
IPC_LOCK,
|
||||
IPC_OWNER,
|
||||
KILL,
|
||||
LEASE,
|
||||
LINUX_IMMUTABLE,
|
||||
MAC_ADMIN,
|
||||
MAC_OVERRIDE,
|
||||
MKNOD,
|
||||
NET_ADMIN,
|
||||
NET_BIND_SERVICE,
|
||||
NET_BROADCAST,
|
||||
NET_RAW,
|
||||
SETFCAP,
|
||||
SETGID,
|
||||
SETPCAP,
|
||||
SETUID,
|
||||
SYSLOG,
|
||||
SYS_ADMIN,
|
||||
SYS_BOOT,
|
||||
SYS_CHROOT,
|
||||
SYS_MODULE,
|
||||
SYS_NICE,
|
||||
SYS_PACCT,
|
||||
SYS_PTRACE,
|
||||
SYS_RAWIO,
|
||||
SYS_RESOURCE,
|
||||
SYS_TIME,
|
||||
SYS_TTY_CONFIG,
|
||||
WAKE_ALARM;
|
||||
}
|
||||
@ -76,6 +76,19 @@ public class ImagePullStatistic implements Comparable<ImagePullStatistic> {
|
||||
}
|
||||
|
||||
public enum StatGroupMode {
|
||||
Day, Week, Month;
|
||||
|
||||
Day("hour"),
|
||||
Week("day"),
|
||||
Month("day");
|
||||
|
||||
private final String dataPoints;
|
||||
|
||||
StatGroupMode(final String dataPoints) {
|
||||
this.dataPoints = dataPoints;
|
||||
}
|
||||
|
||||
public final String getDataPoint() {
|
||||
return dataPoints;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,12 +45,14 @@ public interface Locations {
|
||||
String Schedule = "schedule";
|
||||
String Sync = "sync";
|
||||
String Stats = "stats";
|
||||
String Track = "track";
|
||||
}
|
||||
|
||||
interface Admin {
|
||||
|
||||
String Repositories = "/admin/repositories";
|
||||
String Images = "/admin/images";
|
||||
String ImageEdit = "/admin/image";
|
||||
String Schedules = "/admin/schedules";
|
||||
String Users = "/admin/users";
|
||||
}
|
||||
|
||||
@ -64,6 +64,7 @@ public class WebRouteController {
|
||||
|
||||
get(Locations.Admin.Repositories, new AdminRepositoryController(app), roles(AppRole.Anyone));
|
||||
get(Locations.Admin.Images, new AdminImageController( app), roles(AppRole.Anyone));
|
||||
get(Locations.Admin.ImageEdit, new AdminImageEditController( app), roles(AppRole.Anyone));
|
||||
get(Locations.Admin.Schedules, new AdminScheduleController( app), roles(AppRole.Anyone));
|
||||
|
||||
path(Locations.Internal.Api, () -> {
|
||||
@ -90,6 +91,10 @@ public class WebRouteController {
|
||||
path(Locations.Internal.Stats, () -> {
|
||||
get(apiController::getImagePullHistory, roles(AppRole.Anyone));
|
||||
});
|
||||
|
||||
path(Locations.Internal.Track, () -> {
|
||||
put(apiController::trackNewBranch, roles(AppRole.Anyone));
|
||||
});
|
||||
});
|
||||
|
||||
path(Locations.Internal.Schedule, () -> {
|
||||
|
||||
@ -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.v2.web.routes;
|
||||
|
||||
import io.javalin.http.Context;
|
||||
import io.linuxserver.fleet.core.FleetAppController;
|
||||
import io.linuxserver.fleet.v2.key.ImageKey;
|
||||
import io.linuxserver.fleet.v2.service.RepositoryService;
|
||||
import io.linuxserver.fleet.v2.types.docker.DockerCapability;
|
||||
import io.linuxserver.fleet.v2.web.PageModelSpec;
|
||||
|
||||
public class AdminImageEditController extends AbstractPageHandler {
|
||||
|
||||
private RepositoryService repositoryService;
|
||||
|
||||
public AdminImageEditController(final FleetAppController controller) {
|
||||
super(controller);
|
||||
repositoryService = controller.getRepositoryService();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PageModelSpec handlePageLoad(final Context ctx) {
|
||||
|
||||
final String imageKeyParam = ctx.queryParam("imageKey");
|
||||
if (null != imageKeyParam) {
|
||||
|
||||
final PageModelSpec modelSpec = new PageModelSpec("views/pages/admin/image-edit.ftl");
|
||||
modelSpec.addModelAttribute("image", repositoryService.getImage(ImageKey.parse(imageKeyParam)));
|
||||
modelSpec.addModelAttribute("containerCapabilities", DockerCapability.values());
|
||||
return modelSpec;
|
||||
|
||||
} else {
|
||||
return new PageModelSpec("views/pages/not-found.ftl");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PageModelSpec handleFormSubmission(final Context ctx) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -172,7 +172,21 @@ public class InternalApiController extends AbstractAppService {
|
||||
final ImagePullStatistic.StatGroupMode groupMode = ctx.queryParam("groupMode", ImagePullStatistic.StatGroupMode.class).get();
|
||||
final Image cachedImage = getController().getRepositoryService().getImage(ImageKey.parse(imageKeyParam));
|
||||
|
||||
ctx.json(new ApiImagePullHistoryWrapper(cachedImage.getMetaData().getHistoryFor(groupMode)));
|
||||
ctx.json(new ApiImagePullHistoryWrapper(cachedImage.getMetaData().getHistoryFor(groupMode), groupMode));
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new ApiException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public void trackNewBranch(final Context ctx) {
|
||||
|
||||
try {
|
||||
|
||||
final String imageKeyParam = ctx.formParam("imageKey", String.class).get();
|
||||
final String branchName = ctx.formParam("branchName", String.class).get();
|
||||
|
||||
getController().trackBranch(ImageKey.parse(imageKeyParam), branchName);
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new ApiException(e.getMessage(), e);
|
||||
|
||||
@ -24,4 +24,5 @@ VALUES
|
||||
('SyncAllCachedImages', '1:hours', '0:minutes', 'io.linuxserver.fleet.v2.thread.schedule.sync.AllImagesSyncSchedule'),
|
||||
('GetMissingImages', '30:minutes', '0:minutes', 'io.linuxserver.fleet.v2.thread.schedule.sync.GetMissingImagesSchedule'),
|
||||
('RefreshCache', '1:days', '15:minutes', 'io.linuxserver.fleet.v2.thread.schedule.cache.RefreshCacheSchedule'),
|
||||
('TidyHistoricData', '1:days', '0:minutes', 'io.linuxserver.fleet.v2.thread.schedule.TidyHistoricDataSchedule'),
|
||||
('CheckAppVersion', '1:days', '0:minutes', 'io.linuxserver.fleet.v2.thread.schedule.CheckAppVersionSchedule');
|
||||
|
||||
@ -187,6 +187,23 @@ var adminManager = (function($) {
|
||||
ajaxManager.call(request, function() {});
|
||||
};
|
||||
|
||||
var trackNewBranch = function(branchName, imageKey) {
|
||||
|
||||
var request = {
|
||||
|
||||
url: '/internalapi/image/track',
|
||||
method: 'put',
|
||||
data: {
|
||||
'imageKey': imageKey,
|
||||
'branchName': branchName
|
||||
}
|
||||
};
|
||||
|
||||
ajaxManager.call(request, function() {
|
||||
window.location.reload();
|
||||
});
|
||||
};
|
||||
|
||||
var cleanEmpty = function(val) {
|
||||
return (typeof val === 'undefined' || $.trim(val).length === 0) ? null : val;
|
||||
};
|
||||
@ -233,6 +250,14 @@ var adminManager = (function($) {
|
||||
$('.sync-image').on('click', function() {
|
||||
syncImage($(this));
|
||||
});
|
||||
|
||||
$('#TrackNewBranch').on('click', function() {
|
||||
|
||||
var branchName = $.trim($('#NewTrackedBranch').val());
|
||||
if (branchName.length > 0) {
|
||||
trackNewBranch(branchName, $('#ImageKey').val());
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@ -268,6 +268,19 @@ var imageSearchManager = (function($) {
|
||||
|
||||
var chartManager = (function($) {
|
||||
|
||||
var formatNumber = function(num) {
|
||||
|
||||
var array = num.toString().split('');
|
||||
var index = -3;
|
||||
|
||||
while (array.length + index > 0) {
|
||||
array.splice(index, 0, ',');
|
||||
index -= 4;
|
||||
}
|
||||
|
||||
return array.join('');
|
||||
};
|
||||
|
||||
var populateChart = function(imageKey, groupMode) {
|
||||
|
||||
var request = {
|
||||
@ -278,16 +291,29 @@ var chartManager = (function($) {
|
||||
|
||||
ajaxManager.call(request, function(history) {
|
||||
|
||||
var ctx = document.getElementById('ImagePullHistory').getContext('2d');
|
||||
$('#PullActivityDataPoint').text(history.groupModeDataPoint);
|
||||
$('#PullActivityRate').text(formatNumber(history.mean));
|
||||
|
||||
var ctx = document.getElementById('ImagePullHistory').getContext('2d');
|
||||
var gradient = ctx.createLinearGradient(0, 0, 0, 400);
|
||||
gradient.addColorStop(0, 'rgba(0, 209, 178, 0.5)');
|
||||
gradient.addColorStop(0.3, 'rgba(0, 209, 178, 0)');
|
||||
|
||||
new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: history.pullDifferential.labels,
|
||||
datasets: [{
|
||||
data: history.pullDifferential.pulls,
|
||||
borderColor: 'rgba(0, 209, 178, 1)',
|
||||
backgroundColor: 'rgba(0, 209, 178, 0.3)'
|
||||
}]
|
||||
datasets: [
|
||||
{
|
||||
lineTension: 0,
|
||||
data: history.pullDifferential.pulls,
|
||||
pointRadius: 0,
|
||||
pointHitRadius: 2,
|
||||
borderWidth: 2,
|
||||
borderColor: 'rgba(0, 209, 178, 1)',
|
||||
backgroundColor : gradient
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#Fri Jan 03 17:22:49 GMT 2020
|
||||
app.build.date=2020-01-03T17\:22\:49
|
||||
#Sat Jan 04 18:27:43 GMT 2020
|
||||
app.build.date=2020-01-04T18\:27\:43
|
||||
app.build.os=Linux
|
||||
app.build.user=josh
|
||||
app.version=2.0.0
|
||||
|
||||
270
src/main/resources/views/pages/admin/image-edit.ftl
Normal file
270
src/main/resources/views/pages/admin/image-edit.ftl
Normal file
@ -0,0 +1,270 @@
|
||||
<#--
|
||||
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/>.
|
||||
-->
|
||||
<#import "../../prebuilt/base.ftl" as base />
|
||||
<#import "../../prebuilt/fleet-title.ftl" as title />
|
||||
|
||||
<#import "../../ui/components/dropdown.ftl" as dropdown />
|
||||
<#import "../../ui/layout/section.ftl" as section />
|
||||
<#import "../../ui/layout/container.ftl" as container />
|
||||
<#import "../../ui/form/input.ftl" as input />
|
||||
<#import "../../ui/elements/button.ftl" as button />
|
||||
<#import "../../ui/elements/table.ftl" as table />
|
||||
|
||||
<@base.base title='Edit ${image.name} | Admin' context="admin_image_edit">
|
||||
|
||||
<#if image?has_content>
|
||||
|
||||
<input type="hidden" id="ImageKey" value="${image.key}" />
|
||||
|
||||
<@section.section id="ManageImage">
|
||||
<@container.container>
|
||||
|
||||
<div class="columns is-multiline">
|
||||
|
||||
<div class="column is-12">
|
||||
<@title.title icon="cube" thinValue=image.repositoryName boldValue=image.name separator="/" subtitle="Update metadata and tracked branches" />
|
||||
</div>
|
||||
|
||||
<#--
|
||||
Tag branches
|
||||
-->
|
||||
<div class="column is-half-desktop is-full-mobile has-margin-top">
|
||||
|
||||
<h2 class="title is-4">Tracked Tag Branches</h2>
|
||||
<@table.table id="ImageTrackedBranches" isScrollable=true isFullWidth=true>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Branch Name</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<#list image.tagBranches as tagBranch>
|
||||
<tr>
|
||||
<td class="is-vcentered">${tagBranch.branchName}</td>
|
||||
<td>
|
||||
<#if !tagBranch.branchProtected>
|
||||
<@button.buttons isRightAligned=true>
|
||||
<@button.button extraClasses="remove-tag-branch" colour="danger" size="small">
|
||||
<i class="fas fa-trash"></i> Remove
|
||||
</@button.button>
|
||||
</@button.buttons>
|
||||
<#else>
|
||||
<@button.buttons isRightAligned=true>
|
||||
<@button.button colour="light" size="small" isDisabled=true>
|
||||
Protected
|
||||
</@button.button>
|
||||
</@button.buttons>
|
||||
</#if>
|
||||
</td>
|
||||
</tr>
|
||||
</#list>
|
||||
<tr>
|
||||
<td>
|
||||
<@input.text id="NewTrackedBranch" icon="sitemap" size="small" />
|
||||
</td>
|
||||
<td>
|
||||
<@button.buttons isRightAligned=true>
|
||||
<@button.button id="TrackNewBranch" size="small" colour="success">
|
||||
<i class="fas fa-plus"></i> Track
|
||||
</@button.button>
|
||||
</@button.buttons>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</@table.table>
|
||||
|
||||
</div>
|
||||
|
||||
<#--
|
||||
General base information which is to be added manually (data which can't necessarily be inferred from upstream)
|
||||
-->
|
||||
<div class="column is-12 has-margin-top">
|
||||
<h2 class="title is-4">General</h2>
|
||||
</div>
|
||||
<div class="column is-half-desktop is-full-tablet is-full-mobile">
|
||||
<@input.text id="ImageBase" label="Base Image" />
|
||||
</div>
|
||||
<div class="column is-half-desktop is-full-tablet is-full-mobile">
|
||||
<@input.text id="ImageCategory" label="Category" />
|
||||
</div>
|
||||
<div class="column is-half-desktop is-full-tablet is-full-mobile">
|
||||
<@input.text id="ImageSupportUrl" label="Support Url" />
|
||||
</div>
|
||||
<div class="column is-half-desktop is-full-tablet is-full-mobile">
|
||||
<@input.text id="ImageAppUrl" label="Application Url" />
|
||||
</div>
|
||||
|
||||
<#--
|
||||
A display logo for the grid listing and main image display page
|
||||
-->
|
||||
<div class="column is-half-desktop is-full-tablet is-full-mobile has-margin-top">
|
||||
|
||||
<h2 class="title is-4">App Logo</h2>
|
||||
|
||||
<div class="file has-name is-boxed">
|
||||
<label class="file-label">
|
||||
<input class="file-input" type="file" name="ImageLogo" id="ImageLogo" />
|
||||
<span class="file-cta">
|
||||
<span class="file-icon">
|
||||
<i class="fas fa-upload"></i>
|
||||
</span>
|
||||
<span class="file-label">
|
||||
Choose a file...
|
||||
</span>
|
||||
</span>
|
||||
<span class="file-name">
|
||||
Screen Shot 2017-07-29 at 15.54.25.png
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<#--
|
||||
Port/Volume mappings for containers created from this image
|
||||
-->
|
||||
<div class="column is-12 has-margin-top">
|
||||
|
||||
<h2 class="title is-4">Container Template</h2>
|
||||
<h3 class="title is-5">Recommended Runtime</h3>
|
||||
</div>
|
||||
<div class="column is-12">
|
||||
<@input.text id="ImageTemplateUpstreamUrl" label="Registry Url" />
|
||||
</div>
|
||||
<div class="column is-3-desktop is-12-tablet is-12-mobile">
|
||||
<@input.dropdown label="Restart Policy" id="ImageTemplateRestartPolicy">
|
||||
<option value="no">no</option>
|
||||
<option value="always">always</option>
|
||||
<option value="unless-stopped">unless-stopped</option>
|
||||
<option value="on-failure">on-failure</option>
|
||||
</@input.dropdown>
|
||||
</div>
|
||||
<div class="column is-3-desktop is-12-tablet is-12-mobile">
|
||||
<@input.toggle id="ImageTemplateNetworkHost" label="Host Network" size="large" />
|
||||
</div>
|
||||
<div class="column is-3-desktop is-12-tablet is-12-mobile">
|
||||
<@input.toggle id="ImageTemplatePrivileged" label="Privileged" size="large" />
|
||||
</div>
|
||||
<div class="column is-3-desktop is-12-tablet is-12-mobile">
|
||||
<@input.dropdown id="ImageTemplateCapabilities" label="Capabilities" isMultiple=true>
|
||||
<#list containerCapabilities as capability>
|
||||
<option value="${capability}">${capability}</option>
|
||||
</#list>
|
||||
</@input.dropdown>
|
||||
</div>
|
||||
<div class="column is-12 has-margin-top">
|
||||
|
||||
<h3 class="title is-5">Ports</h3>
|
||||
<@table.table id="ImageTemplatePorts" isScrollable=true isFullWidth=true>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Port</th>
|
||||
<th>Protocol</th>
|
||||
<th>Description</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</@table.table>
|
||||
<@button.buttons isRightAligned=true>
|
||||
<@button.button id="AddNewPort" colour="normal-colour" size="small">
|
||||
<i class="fas fa-plus has-text-success"></i> Add
|
||||
</@button.button>
|
||||
</@button.buttons>
|
||||
|
||||
</div>
|
||||
<div class="column is-12 has-margin-top">
|
||||
|
||||
<h3 class="title is-5">Volumes</h3>
|
||||
<@table.table id="ImageTemplateVolumes" isScrollable=true isFullWidth=true>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Volume</th>
|
||||
<th>Read Only?</th>
|
||||
<th>Description</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</@table.table>
|
||||
<@button.buttons isRightAligned=true>
|
||||
<@button.button id="AddNewVolume" colour="normal-colour" size="small">
|
||||
<i class="fas fa-plus has-text-success"></i> Add
|
||||
</@button.button>
|
||||
</@button.buttons>
|
||||
|
||||
</div>
|
||||
<div class="column is-12 has-margin-top">
|
||||
|
||||
<h3 class="title is-5">Environment</h3>
|
||||
<@table.table id="ImageTemplateEnv" isScrollable=true isFullWidth=true>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Environment Variable</th>
|
||||
<th>Description</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</@table.table>
|
||||
<@button.buttons isRightAligned=true>
|
||||
<@button.button id="AddNewEnv" colour="normal-colour" size="small">
|
||||
<i class="fas fa-plus has-text-success"></i> Add
|
||||
</@button.button>
|
||||
</@button.buttons>
|
||||
|
||||
</div>
|
||||
<div class="column is-12 has-margin-top">
|
||||
|
||||
<h3 class="title is-5">Devices</h3>
|
||||
<@table.table id="ImageTemplateDevices" isScrollable=true isFullWidth=true>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Device</th>
|
||||
<th>Description</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</@table.table>
|
||||
<@button.buttons isRightAligned=true>
|
||||
<@button.button id="AddNewDevice" colour="normal-colour" size="small">
|
||||
<i class="fas fa-plus has-text-success"></i> Add
|
||||
</@button.button>
|
||||
</@button.buttons>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</@container.container>
|
||||
</@section.section>
|
||||
|
||||
<#else>
|
||||
|
||||
<@section.section id="ManageImages">
|
||||
<@container.container>
|
||||
Unable to find repository.
|
||||
</@container.container>
|
||||
</@section.section>
|
||||
|
||||
</#if>
|
||||
|
||||
</@base.base>
|
||||
@ -75,7 +75,7 @@
|
||||
<@button.button id="ForceResync_${image.key.id}" size="small" title="Force resync" colour="normal-colour" extraAttributes='data-image-key="${image.key}"' extraClasses="sync-image">
|
||||
<i class="fas fa-sync-alt is-marginless"></i>
|
||||
</@button.button>
|
||||
<@button.link size="small" title="Edit image metadata" colour="normal-colour" link="/admin/image?key=${image.fullName}">
|
||||
<@button.link size="small" title="Edit image metadata" colour="normal-colour" link="/admin/image?imageKey=${image.key}">
|
||||
<i class="fas fa-pencil-alt is-marginless"></i>
|
||||
</@button.link>
|
||||
</@button.buttons>
|
||||
|
||||
@ -83,26 +83,34 @@
|
||||
</div>
|
||||
|
||||
<div class="column is-6-desktop is-12-tablet">
|
||||
<@box.box extraClasses="is-paddingless is-clipped is-relative">
|
||||
<@box.box>
|
||||
|
||||
<h2 class="title is-5 has-text-centered has-margin-top">Pull Activity</h2>
|
||||
<h2 class="title is-5 has-text-centered">Pull Activity</h2>
|
||||
|
||||
<div class="tabs is-toggle is-centered is-small is-marginless">
|
||||
<ul class="is-marginless">
|
||||
<li data-group-mode="Day">
|
||||
<a><span>1d</span></a>
|
||||
</li>
|
||||
<li class="is-active" data-group-mode="Week">
|
||||
<li data-group-mode="Week">
|
||||
<a><span>1w</span></a>
|
||||
</li>
|
||||
<li data-group-mode="Month">
|
||||
<li class="is-active" data-group-mode="Month">
|
||||
<a><span>1m</span></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="chart-container" style="position: relative; width: 100%; height: 200px">
|
||||
<canvas id="ImagePullHistory"></canvas>
|
||||
<div class="columns has-margin-top">
|
||||
<div class="column is-half-desktop is-full-mobile has-text-centered is-vcentered">
|
||||
<h4 class="title is-6">Pulls per <span id="PullActivityDataPoint"></span></h4>
|
||||
<@tag.tag value='<span id="PullActivityRate"></span>' colour="light" />
|
||||
</div>
|
||||
<div class="column is-half-desktop is-full-mobile">
|
||||
<div class="chart-container" style="position: relative; width: 100%; height: 150px">
|
||||
<canvas id="ImagePullHistory"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</@box.box>
|
||||
|
||||
@ -112,7 +112,7 @@
|
||||
</#if>
|
||||
|
||||
<#if context=='image'>
|
||||
chartManager.populateChart('${image.key}', 'Week');
|
||||
chartManager.populateChart('${image.key}', 'Month');
|
||||
</#if>
|
||||
|
||||
appManager.init();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user