Fork project
This commit is contained in:
27
assets/config.js
Normal file
27
assets/config.js
Normal file
@ -0,0 +1,27 @@
|
||||
const $ = CTFd.lib.$;
|
||||
|
||||
$(".config-section > form:not(.form-upload)").submit(async function (event) {
|
||||
event.preventDefault();
|
||||
const obj = $(this).serializeJSON();
|
||||
const params = {};
|
||||
for (let x in obj) {
|
||||
if (obj[x] === "true") {
|
||||
params[x] = true;
|
||||
} else if (obj[x] === "false") {
|
||||
params[x] = false;
|
||||
} else {
|
||||
params[x] = obj[x];
|
||||
}
|
||||
}
|
||||
params['whale:refresh'] = btoa(+new Date).slice(-7, -2);
|
||||
|
||||
await CTFd.api.patch_config_list({}, params);
|
||||
location.reload();
|
||||
});
|
||||
$(".config-section > form:not(.form-upload) > div > div > div > #router-type").change(async function () {
|
||||
await CTFd.api.patch_config_list({}, {
|
||||
'whale:router_type': $(this).val(),
|
||||
'whale:refresh': btoa(+new Date).slice(-7, -2),
|
||||
});
|
||||
location.reload();
|
||||
});
|
||||
120
assets/containers.js
Normal file
120
assets/containers.js
Normal file
@ -0,0 +1,120 @@
|
||||
const $ = CTFd.lib.$;
|
||||
|
||||
function htmlentities(str) {
|
||||
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||
}
|
||||
|
||||
function copyToClipboard(event, str) {
|
||||
// Select element
|
||||
const el = document.createElement('textarea');
|
||||
el.value = str;
|
||||
el.setAttribute('readonly', '');
|
||||
el.style.position = 'absolute';
|
||||
el.style.left = '-9999px';
|
||||
document.body.appendChild(el);
|
||||
el.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(el);
|
||||
|
||||
$(event.target).tooltip({
|
||||
title: "Copied!",
|
||||
trigger: "manual"
|
||||
});
|
||||
$(event.target).tooltip("show");
|
||||
|
||||
setTimeout(function () {
|
||||
$(event.target).tooltip("hide");
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
$(".click-copy").click(function (e) {
|
||||
copyToClipboard(e, $(this).data("copy"));
|
||||
})
|
||||
|
||||
async function delete_container(user_id) {
|
||||
let response = await CTFd.fetch("/api/v1/plugins/ctfd-whale/admin/container?user_id=" + user_id, {
|
||||
method: "DELETE",
|
||||
credentials: "same-origin",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
});
|
||||
response = await response.json();
|
||||
return response.success;
|
||||
}
|
||||
async function renew_container(user_id) {
|
||||
let response = await CTFd.fetch(
|
||||
"/api/v1/plugins/ctfd-whale/admin/container?user_id=" + user_id, {
|
||||
method: "PATCH",
|
||||
credentials: "same-origin",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
});
|
||||
response = await response.json();
|
||||
return response.success;
|
||||
}
|
||||
|
||||
$('#containers-renew-button').click(function (e) {
|
||||
let users = $("input[data-user-id]:checked").map(function () {
|
||||
return $(this).data("user-id");
|
||||
});
|
||||
CTFd.ui.ezq.ezQuery({
|
||||
title: "Renew Containers",
|
||||
body: `Are you sure you want to renew the selected ${users.length} container(s)?`,
|
||||
success: async function () {
|
||||
await Promise.all(users.toArray().map((user) => renew_container(user)));
|
||||
location.reload();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#containers-delete-button').click(function (e) {
|
||||
let users = $("input[data-user-id]:checked").map(function () {
|
||||
return $(this).data("user-id");
|
||||
});
|
||||
CTFd.ui.ezq.ezQuery({
|
||||
title: "Delete Containers",
|
||||
body: `Are you sure you want to delete the selected ${users.length} container(s)?`,
|
||||
success: async function () {
|
||||
await Promise.all(users.toArray().map((user) => delete_container(user)));
|
||||
location.reload();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(".delete-container").click(function (e) {
|
||||
e.preventDefault();
|
||||
let container_id = $(this).attr("container-id");
|
||||
let user_id = $(this).attr("user-id");
|
||||
|
||||
CTFd.ui.ezq.ezQuery({
|
||||
title: "Destroy Container",
|
||||
body: "<span>Are you sure you want to delete <strong>Container #{0}</strong>?</span>".format(
|
||||
htmlentities(container_id)
|
||||
),
|
||||
success: async function () {
|
||||
await delete_container(user_id);
|
||||
location.reload();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(".renew-container").click(function (e) {
|
||||
e.preventDefault();
|
||||
let container_id = $(this).attr("container-id");
|
||||
let user_id = $(this).attr("user-id");
|
||||
|
||||
CTFd.ui.ezq.ezQuery({
|
||||
title: "Renew Container",
|
||||
body: "<span>Are you sure you want to renew <strong>Container #{0}</strong>?</span>".format(
|
||||
htmlentities(container_id)
|
||||
),
|
||||
success: async function () {
|
||||
await renew_container(user_id);
|
||||
location.reload();
|
||||
},
|
||||
});
|
||||
});
|
||||
100
assets/create.html
Normal file
100
assets/create.html
Normal file
@ -0,0 +1,100 @@
|
||||
{% extends "admin/challenges/create.html" %}
|
||||
|
||||
{% block header %}
|
||||
<div class="alert alert-secondary" role="alert">
|
||||
Dynamic docker challenge allows players to deploy their per-challenge standalone instances.
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block value %}
|
||||
<div class="form-group">
|
||||
<label for="value">Docker Image<br>
|
||||
<small class="form-text text-muted">
|
||||
The docker image used to deploy
|
||||
</small>
|
||||
</label>
|
||||
<input type="text" class="form-control" name="docker_image" placeholder="Enter docker image name" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="value">Frp Redirect Type<br>
|
||||
<small class="form-text text-muted">
|
||||
Decide the redirect type how frp redirect traffic
|
||||
</small>
|
||||
</label>
|
||||
<select class="form-control" name="redirect_type">
|
||||
<option value="http" selected>HTTP</option>
|
||||
<option value="direct">Direct</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="value">Frp Redirect Port<br>
|
||||
<small class="form-text text-muted">
|
||||
Decide which port in the instance that frp should redirect traffic for
|
||||
</small>
|
||||
</label>
|
||||
<input type="number" class="form-control" name="redirect_port" placeholder="Enter the port you want to open"
|
||||
required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="value">Docker Container Memory Limit<br>
|
||||
<small class="form-text text-muted">
|
||||
The memory usage limit
|
||||
</small>
|
||||
</label>
|
||||
<input type="text" class="form-control" name="memory_limit" placeholder="Enter the memory limit" value="128m"
|
||||
required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="value">Docker Container CPU Limit<br>
|
||||
<small class="form-text text-muted">
|
||||
The CPU usage limit
|
||||
</small>
|
||||
</label>
|
||||
<input type="number" class="form-control" name="cpu_limit" placeholder="Enter the cpu limit" value="0.5"
|
||||
required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="value">Initial Value<br>
|
||||
<small class="form-text text-muted">
|
||||
This is how many points the challenge is worth initially.
|
||||
</small>
|
||||
</label>
|
||||
<input type="number" class="form-control" name="value" placeholder="Enter value" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="value">Decay Limit<br>
|
||||
<small class="form-text text-muted">
|
||||
The amount of solves before the challenge reaches its minimum value
|
||||
</small>
|
||||
</label>
|
||||
<input type="number" class="form-control" name="decay" placeholder="Enter decay limit" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="value">Minimum Value<br>
|
||||
<small class="form-text text-muted">
|
||||
This is the lowest that the challenge can be worth
|
||||
</small>
|
||||
</label>
|
||||
<input type="number" class="form-control" name="minimum" placeholder="Enter minimum value" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="value">Score Type<br>
|
||||
<small class="form-text text-muted">
|
||||
Decide it use dynamic score or not
|
||||
</small>
|
||||
</label>
|
||||
|
||||
<select class="form-control" name="dynamic_score">
|
||||
<option value="0" selected>Static Score</option>
|
||||
<option value="1">Dynamic Score</option>
|
||||
</select>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block type %}
|
||||
<input type="hidden" value="dynamic_docker" name="type" id="chaltype">
|
||||
{% endblock %}
|
||||
30
assets/create.js
Normal file
30
assets/create.js
Normal file
@ -0,0 +1,30 @@
|
||||
// Markdown Preview
|
||||
if ($ === undefined) $ = CTFd.lib.$
|
||||
$('#desc-edit').on('shown.bs.tab', function(event) {
|
||||
if (event.target.hash == '#desc-preview') {
|
||||
var editor_value = $('#desc-editor').val();
|
||||
$(event.target.hash).html(
|
||||
CTFd._internal.challenge.render(editor_value)
|
||||
);
|
||||
}
|
||||
});
|
||||
$('#new-desc-edit').on('shown.bs.tab', function(event) {
|
||||
if (event.target.hash == '#new-desc-preview') {
|
||||
var editor_value = $('#new-desc-editor').val();
|
||||
$(event.target.hash).html(
|
||||
CTFd._internal.challenge.render(editor_value)
|
||||
);
|
||||
}
|
||||
});
|
||||
$("#solve-attempts-checkbox").change(function() {
|
||||
if (this.checked) {
|
||||
$('#solve-attempts-input').show();
|
||||
} else {
|
||||
$('#solve-attempts-input').hide();
|
||||
$('#max_attempts').val('');
|
||||
}
|
||||
});
|
||||
|
||||
$(document).ready(function() {
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
});
|
||||
94
assets/update.html
Normal file
94
assets/update.html
Normal file
@ -0,0 +1,94 @@
|
||||
{% extends "admin/challenges/update.html" %}
|
||||
|
||||
{% block value %}
|
||||
<div class="form-group">
|
||||
<label for="value">Current Value<br>
|
||||
<small class="form-text text-muted">
|
||||
This is how many points the challenge is worth right now.
|
||||
</small>
|
||||
</label>
|
||||
<input type="number" class="form-control chal-value" name="value" value="{{ challenge.value }}" disabled>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="value">Initial Value<br>
|
||||
<small class="form-text text-muted">
|
||||
This is how many points the challenge was worth initially.
|
||||
</small>
|
||||
</label>
|
||||
<input type="number" class="form-control chal-initial" name="initial" value="{{ challenge.initial }}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="value">Decay Limit<br>
|
||||
<small class="form-text text-muted">
|
||||
The amount of solves before the challenge reaches its minimum value
|
||||
</small>
|
||||
</label>
|
||||
<input type="number" class="form-control chal-decay" name="decay" value="{{ challenge.decay }}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="value">Minimum Value<br>
|
||||
<small class="form-text text-muted">
|
||||
This is the lowest that the challenge can be worth
|
||||
</small>
|
||||
</label>
|
||||
<input type="number" class="form-control chal-minimum" name="minimum" value="{{ challenge.minimum }}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="value">Docker Image<br>
|
||||
<small class="form-text text-muted">
|
||||
The docker image used to deploy
|
||||
</small>
|
||||
</label>
|
||||
<input type="text" class="form-control" name="docker_image" placeholder="Enter docker image name"
|
||||
required value="{{ challenge.docker_image }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="value">Frp Redirect Type<br>
|
||||
<small class="form-text text-muted">
|
||||
Decide the redirect type how frp redirect traffic
|
||||
</small>
|
||||
</label>
|
||||
<select class="form-control" name="redirect_type">
|
||||
<option value="http" {% if challenge.redirect_type == "http" %}selected{% endif %}>HTTP</option>
|
||||
<option value="direct" {% if challenge.redirect_type == "direct" %}selected{% endif %}>Direct</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="value">Frp Redirect Port<br>
|
||||
<small class="form-text text-muted">
|
||||
Decide which port in the instance that frp should redirect traffic for
|
||||
</small>
|
||||
</label>
|
||||
<input type="number" class="form-control" name="redirect_port" placeholder="Enter the port you want to open"
|
||||
required value="{{ challenge.redirect_port }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="value">Docker Container Memory Limit<br>
|
||||
<small class="form-text text-muted">
|
||||
The memory usage limit
|
||||
</small>
|
||||
</label>
|
||||
<input type="text" class="form-control" name="memory_limit" placeholder="Enter the memory limit"
|
||||
value="{{ challenge.memory_limit }}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="value">Docker Container CPU Limit<br>
|
||||
<small class="form-text text-muted">
|
||||
The CPU usage limit
|
||||
</small>
|
||||
</label>
|
||||
<input type="number" class="form-control" name="cpu_limit" placeholder="Enter the cpu limit"
|
||||
value="{{ challenge.cpu_limit }}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="value">Score Type<br>
|
||||
<small class="form-text text-muted">
|
||||
Decide it use dynamic score or not
|
||||
</small>
|
||||
</label>
|
||||
<select class="form-control" name="dynamic_score">
|
||||
<option value="0" {% if challenge.dynamic_score == 0 %}selected{% endif %}>Static Score</option>
|
||||
<option value="1" {% if challenge.dynamic_score == 1 %}selected{% endif %}>Dynamic Score</option>
|
||||
</select>
|
||||
</div>
|
||||
{% endblock %}
|
||||
52
assets/update.js
Normal file
52
assets/update.js
Normal file
@ -0,0 +1,52 @@
|
||||
if ($ === undefined) $ = CTFd.lib.$
|
||||
$('#submit-key').click(function(e) {
|
||||
submitkey($('#chalid').val(), $('#answer').val())
|
||||
});
|
||||
|
||||
$('#submit-keys').click(function(e) {
|
||||
e.preventDefault();
|
||||
$('#update-keys').modal('hide');
|
||||
});
|
||||
|
||||
$('#limit_max_attempts').change(function() {
|
||||
if (this.checked) {
|
||||
$('#chal-attempts-group').show();
|
||||
} else {
|
||||
$('#chal-attempts-group').hide();
|
||||
$('#chal-attempts-input').val('');
|
||||
}
|
||||
});
|
||||
|
||||
// Markdown Preview
|
||||
$('#desc-edit').on('shown.bs.tab', function(event) {
|
||||
if (event.target.hash == '#desc-preview') {
|
||||
var editor_value = $('#desc-editor').val();
|
||||
$(event.target.hash).html(
|
||||
window.challenge.render(editor_value)
|
||||
);
|
||||
}
|
||||
});
|
||||
$('#new-desc-edit').on('shown.bs.tab', function(event) {
|
||||
if (event.target.hash == '#new-desc-preview') {
|
||||
var editor_value = $('#new-desc-editor').val();
|
||||
$(event.target.hash).html(
|
||||
window.challenge.render(editor_value)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
function loadchal(id, update) {
|
||||
$.get(script_root + '/admin/chal/' + id, function(obj) {
|
||||
$('#desc-write-link').click(); // Switch to Write tab
|
||||
if (typeof update === 'undefined')
|
||||
$('#update-challenge').modal();
|
||||
});
|
||||
}
|
||||
|
||||
function openchal(id) {
|
||||
loadchal(id);
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
});
|
||||
36
assets/view.html
Normal file
36
assets/view.html
Normal file
@ -0,0 +1,36 @@
|
||||
{% extends "challenge.html" %}
|
||||
|
||||
{% block description %}
|
||||
{{ challenge.html }}
|
||||
<div class="row text-center pb-3">
|
||||
<div id="whale-panel" style="width: 100%;">
|
||||
<div id="whale-panel-stopped" class="card" style="width: 100%;">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Instance Info</h5>
|
||||
<button class="btn btn-primary card-link" id="whale-button-boot" type="button"
|
||||
onclick="CTFd._internal.challenge.boot()">Launch an instance</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="whale-panel-started" type="hidden" class="card" style="width: 100%;">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Instance Info</h5>
|
||||
<h6 class="card-subtitle mb-2 text-muted">
|
||||
Remaining Time: <span id="whale-challenge-count-down"></span>s
|
||||
</h6>
|
||||
<h6 class="card-subtitle mb-2 text-muted">
|
||||
Lan Domain: <span id="whale-challenge-lan-domain"></span>
|
||||
</h6>
|
||||
<p id="whale-challenge-user-access" class="card-text"></p>
|
||||
<button type="button" class="btn btn-danger card-link" id="whale-button-destroy"
|
||||
onclick="CTFd._internal.challenge.destroy()">
|
||||
Destroy this instance
|
||||
</button>
|
||||
<button type="button" class="btn btn-success card-link" id="whale-button-renew"
|
||||
onclick="CTFd._internal.challenge.renew()">
|
||||
Renew this instance
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
239
assets/view.js
Normal file
239
assets/view.js
Normal file
@ -0,0 +1,239 @@
|
||||
CTFd._internal.challenge.data = undefined
|
||||
|
||||
CTFd._internal.challenge.renderer = null;
|
||||
|
||||
CTFd._internal.challenge.preRender = function () {
|
||||
}
|
||||
|
||||
CTFd._internal.challenge.render = null;
|
||||
|
||||
CTFd._internal.challenge.postRender = function () {
|
||||
loadInfo();
|
||||
}
|
||||
|
||||
if (window.$ === undefined) window.$ = CTFd.lib.$;
|
||||
|
||||
function loadInfo() {
|
||||
var challenge_id = CTFd._internal.challenge.data.id;
|
||||
var url = "/api/v1/plugins/ctfd-whale/container?challenge_id=" + challenge_id;
|
||||
|
||||
CTFd.fetch(url, {
|
||||
method: 'GET',
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}).then(function (response) {
|
||||
if (response.status === 429) {
|
||||
// User was ratelimited but process response
|
||||
return response.json();
|
||||
}
|
||||
if (response.status === 403) {
|
||||
// User is not logged in or CTF is paused.
|
||||
return response.json();
|
||||
}
|
||||
return response.json();
|
||||
}).then(function (response) {
|
||||
if (window.t !== undefined) {
|
||||
clearInterval(window.t);
|
||||
window.t = undefined;
|
||||
}
|
||||
if (response.success) response = response.data;
|
||||
else CTFd._functions.events.eventAlert({
|
||||
title: "Fail",
|
||||
html: response.message,
|
||||
button: "OK"
|
||||
});
|
||||
if (response.remaining_time != undefined) {
|
||||
$('#whale-challenge-user-access').html(response.user_access);
|
||||
$('#whale-challenge-lan-domain').html(response.lan_domain);
|
||||
$('#whale-challenge-count-down').text(response.remaining_time);
|
||||
$('#whale-panel-stopped').hide();
|
||||
$('#whale-panel-started').show();
|
||||
|
||||
window.t = setInterval(() => {
|
||||
const c = $('#whale-challenge-count-down').text();
|
||||
if (!c) return;
|
||||
let second = parseInt(c) - 1;
|
||||
if (second <= 0) {
|
||||
loadInfo();
|
||||
}
|
||||
$('#whale-challenge-count-down').text(second);
|
||||
}, 1000);
|
||||
} else {
|
||||
$('#whale-panel-started').hide();
|
||||
$('#whale-panel-stopped').show();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
CTFd._internal.challenge.destroy = function () {
|
||||
var challenge_id = CTFd._internal.challenge.data.id;
|
||||
var url = "/api/v1/plugins/ctfd-whale/container?challenge_id=" + challenge_id;
|
||||
|
||||
$('#whale-button-destroy').text("Waiting...");
|
||||
$('#whale-button-destroy').prop('disabled', true);
|
||||
|
||||
var params = {};
|
||||
|
||||
CTFd.fetch(url, {
|
||||
method: 'DELETE',
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(params)
|
||||
}).then(function (response) {
|
||||
if (response.status === 429) {
|
||||
// User was ratelimited but process response
|
||||
return response.json();
|
||||
}
|
||||
if (response.status === 403) {
|
||||
// User is not logged in or CTF is paused.
|
||||
return response.json();
|
||||
}
|
||||
return response.json();
|
||||
}).then(function (response) {
|
||||
if (response.success) {
|
||||
loadInfo();
|
||||
CTFd._functions.events.eventAlert({
|
||||
title: "Success",
|
||||
html: "Your instance has been destroyed!",
|
||||
button: "OK"
|
||||
});
|
||||
} else {
|
||||
CTFd._functions.events.eventAlert({
|
||||
title: "Fail",
|
||||
html: response.message,
|
||||
button: "OK"
|
||||
});
|
||||
}
|
||||
}).finally(() => {
|
||||
$('#whale-button-destroy').text("Destroy this instance");
|
||||
$('#whale-button-destroy').prop('disabled', false);
|
||||
});
|
||||
};
|
||||
|
||||
CTFd._internal.challenge.renew = function () {
|
||||
var challenge_id = CTFd._internal.challenge.data.id;
|
||||
var url = "/api/v1/plugins/ctfd-whale/container?challenge_id=" + challenge_id;
|
||||
|
||||
$('#whale-button-renew').text("Waiting...");
|
||||
$('#whale-button-renew').prop('disabled', true);
|
||||
|
||||
var params = {};
|
||||
|
||||
CTFd.fetch(url, {
|
||||
method: 'PATCH',
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(params)
|
||||
}).then(function (response) {
|
||||
if (response.status === 429) {
|
||||
// User was ratelimited but process response
|
||||
return response.json();
|
||||
}
|
||||
if (response.status === 403) {
|
||||
// User is not logged in or CTF is paused.
|
||||
return response.json();
|
||||
}
|
||||
return response.json();
|
||||
}).then(function (response) {
|
||||
if (response.success) {
|
||||
loadInfo();
|
||||
CTFd._functions.events.eventAlert({
|
||||
title: "Success",
|
||||
html: "Your instance has been renewed!",
|
||||
button: "OK"
|
||||
});
|
||||
} else {
|
||||
CTFd._functions.events.eventAlert({
|
||||
title: "Fail",
|
||||
html: response.message,
|
||||
button: "OK"
|
||||
});
|
||||
}
|
||||
}).finally(() => {
|
||||
$('#whale-button-renew').text("Renew this instance");
|
||||
$('#whale-button-renew').prop('disabled', false);
|
||||
});
|
||||
};
|
||||
|
||||
CTFd._internal.challenge.boot = function () {
|
||||
var challenge_id = CTFd._internal.challenge.data.id;
|
||||
var url = "/api/v1/plugins/ctfd-whale/container?challenge_id=" + challenge_id;
|
||||
|
||||
$('#whale-button-boot').text("Waiting...");
|
||||
$('#whale-button-boot').prop('disabled', true);
|
||||
|
||||
var params = {};
|
||||
|
||||
CTFd.fetch(url, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(params)
|
||||
}).then(function (response) {
|
||||
if (response.status === 429) {
|
||||
// User was ratelimited but process response
|
||||
return response.json();
|
||||
}
|
||||
if (response.status === 403) {
|
||||
// User is not logged in or CTF is paused.
|
||||
return response.json();
|
||||
}
|
||||
return response.json();
|
||||
}).then(function (response) {
|
||||
if (response.success) {
|
||||
loadInfo();
|
||||
CTFd._functions.events.eventAlert({
|
||||
title: "Success",
|
||||
html: "Your instance has been deployed!",
|
||||
button: "OK"
|
||||
});
|
||||
} else {
|
||||
CTFd._functions.events.eventAlert({
|
||||
title: "Fail",
|
||||
html: response.message,
|
||||
button: "OK"
|
||||
});
|
||||
}
|
||||
}).finally(() => {
|
||||
$('#whale-button-boot').text("Launch an instance");
|
||||
$('#whale-button-boot').prop('disabled', false);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
CTFd._internal.challenge.submit = function (preview) {
|
||||
var challenge_id = CTFd._internal.challenge.data.id;
|
||||
var submission = $('#challenge-input').val()
|
||||
|
||||
var body = {
|
||||
'challenge_id': challenge_id,
|
||||
'submission': submission,
|
||||
}
|
||||
var params = {}
|
||||
if (preview)
|
||||
params['preview'] = true
|
||||
|
||||
return CTFd.api.post_challenge_attempt(params, body).then(function (response) {
|
||||
if (response.status === 429) {
|
||||
// User was ratelimited but process response
|
||||
return response
|
||||
}
|
||||
if (response.status === 403) {
|
||||
// User is not logged in or CTF is paused.
|
||||
return response
|
||||
}
|
||||
return response
|
||||
})
|
||||
};
|
||||
Reference in New Issue
Block a user