mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2024-08-30 18:23:09 +00:00
Merge branch 'dev' into 'master'
4.0.2 Patches See merge request crafty-controller/crafty-4!357
This commit is contained in:
commit
2bbcf65278
@ -7,6 +7,7 @@ stages:
|
|||||||
- test
|
- test
|
||||||
- prod-deployment
|
- prod-deployment
|
||||||
- dev-deployment
|
- dev-deployment
|
||||||
|
- release
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
DOCKER_HOST: tcp://docker:2376
|
DOCKER_HOST: tcp://docker:2376
|
||||||
@ -16,6 +17,7 @@ include:
|
|||||||
- local: .gitlab/lint.yml
|
- local: .gitlab/lint.yml
|
||||||
- local: .gitlab/docker-build.yml
|
- local: .gitlab/docker-build.yml
|
||||||
- local: .gitlab/windows-build.yml
|
- local: .gitlab/windows-build.yml
|
||||||
|
- local: .gitlab/release.yml
|
||||||
- template: Security/Dependency-Scanning.gitlab-ci.yml
|
- template: Security/Dependency-Scanning.gitlab-ci.yml
|
||||||
- template: Security/SAST.gitlab-ci.yml
|
- template: Security/SAST.gitlab-ci.yml
|
||||||
- template: Security/Secret-Detection.gitlab-ci.yml
|
- template: Security/Secret-Detection.gitlab-ci.yml
|
||||||
|
@ -27,3 +27,4 @@ This checklist encourages us to confirm any changes have been analyzed to reduce
|
|||||||
* [ ] Have you resolved any lint issues?
|
* [ ] Have you resolved any lint issues?
|
||||||
* [ ] Have you assigned a reviewer?
|
* [ ] Have you assigned a reviewer?
|
||||||
* [ ] Have you applied correct labels?
|
* [ ] Have you applied correct labels?
|
||||||
|
* [ ] Have you updated CHANGELOG.md?
|
||||||
|
18
.gitlab/release.yml
Normal file
18
.gitlab/release.yml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# yamllint disable rule:line-length
|
||||||
|
---
|
||||||
|
release:
|
||||||
|
stage: release
|
||||||
|
image: registry.gitlab.com/gitlab-org/release-cli:latest
|
||||||
|
rules:
|
||||||
|
- if: $CI_COMMIT_TAG
|
||||||
|
script:
|
||||||
|
- echo "Running release job for tag $CI_COMMIT_TAG"
|
||||||
|
release:
|
||||||
|
tag_name: $CI_COMMIT_TAG
|
||||||
|
name: Crafty $CI_COMMIT_TAG
|
||||||
|
description: ./CHANGELOG.md
|
||||||
|
assets:
|
||||||
|
links:
|
||||||
|
- name: Windows Package
|
||||||
|
url: "https://gitlab.com/some/repo/-/jobs/$(cat CI_JOB_ID.txt)/artifacts/download"
|
||||||
|
link_type: package
|
@ -50,9 +50,11 @@ win-prod-build:
|
|||||||
- .venv/
|
- .venv/
|
||||||
rules:
|
rules:
|
||||||
- if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH"
|
- if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH"
|
||||||
|
- if: $CI_COMMIT_TAG
|
||||||
environment:
|
environment:
|
||||||
name: production
|
name: production
|
||||||
script:
|
script:
|
||||||
|
- echo "${CI_JOB_ID}" > CI_JOB_ID.txt
|
||||||
- |
|
- |
|
||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
py -m venv .venv
|
py -m venv .venv
|
||||||
@ -79,5 +81,7 @@ win-prod-build:
|
|||||||
paths:
|
paths:
|
||||||
- app\
|
- app\
|
||||||
- .\crafty_commander.exe
|
- .\crafty_commander.exe
|
||||||
|
- CI_JOB_ID.txt
|
||||||
|
expire_in: never
|
||||||
exclude:
|
exclude:
|
||||||
- app\classes\**\*
|
- app\classes\**\*
|
||||||
|
31
CHANGELOG.md
Normal file
31
CHANGELOG.md
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## [4.0.2] - 2022/06/16
|
||||||
|
|
||||||
|
### New features
|
||||||
|
None
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
- Fix winreg import pass on non-NT systems ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/344))
|
||||||
|
- Make the WebSocket automatically reconnect. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/345))
|
||||||
|
- Fix an error when there are no servers ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/346))
|
||||||
|
- Use relative paths for the jarfile and logs ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/347))
|
||||||
|
- Flatten all instances of username creation or editing, usernames should be lower case.
|
||||||
|
- - ([Merge Request 1](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/342))
|
||||||
|
- - ([Merge Request 2](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/351))
|
||||||
|
- Add version inheretence & config check ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/353))
|
||||||
|
- Fix support log temp file deletion issue/hang ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/354))
|
||||||
|
|
||||||
|
## [4.0.1] - 2022/06/15
|
||||||
|
|
||||||
|
### New features
|
||||||
|
None
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
- Remove session.lock warning ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/338))
|
||||||
|
- Correct Dutch Spacing Issue ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/340))
|
||||||
|
- Remove no-else-* pylint exemptions and tidy code. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/342))
|
||||||
|
- Make unRAID more readable, and flatten path to lower, to fit standard practice. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/337))
|
||||||
|
- Fix Java Pathing issues on windows ([Commit](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/343/diffs?commit_id=cda2120579083d447db5dbeb5489822880f4cae7))
|
||||||
|
|
@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
|
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
|
||||||
[![Supported Python Versions](https://shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10%20-blue)](https://www.python.org)
|
[![Supported Python Versions](https://shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10%20-blue)](https://www.python.org)
|
||||||
[![Version(temp-hardcoded)](https://img.shields.io/badge/release-v4.0.1--beta-orange)](https://gitlab.com/crafty-controller/crafty-4)
|
[![Version(temp-hardcoded)](https://img.shields.io/badge/release-v4.0.2--beta-orange)](https://gitlab.com/crafty-controller/crafty-4/-/releases)
|
||||||
[![Code Quality(temp-hardcoded)](https://img.shields.io/badge/code%20quality-10-brightgreen)](https://gitlab.com/crafty-controller/crafty-4)
|
[![Code Quality(temp-hardcoded)](https://img.shields.io/badge/code%20quality-10-brightgreen)](https://gitlab.com/crafty-controller/crafty-4)
|
||||||
[![Build Status](https://gitlab.com/crafty-controller/crafty-4/badges/master/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-4/-/commits/master)
|
[![Build Status](https://gitlab.com/crafty-controller/crafty-4/badges/master/pipeline.svg)](https://gitlab.com/crafty-controller/crafty-4/-/commits/master)
|
||||||
|
|
||||||
# Crafty Controller 4.0.1-beta
|
# Crafty Controller 4.0.2-beta
|
||||||
> Python based Control Panel for your Minecraft Server
|
> Python based Control Panel for your Minecraft Server
|
||||||
|
|
||||||
## What is Crafty Controller?
|
## What is Crafty Controller?
|
||||||
|
@ -82,8 +82,16 @@ class Controller:
|
|||||||
if exec_user["preparing"]:
|
if exec_user["preparing"]:
|
||||||
return
|
return
|
||||||
self.users.set_prepare(exec_user["user_id"])
|
self.users.set_prepare(exec_user["user_id"])
|
||||||
# Delete previous instace of logs
|
logger.info("Checking for previous support logs.")
|
||||||
self.del_support_file(exec_user["support_logs"])
|
if exec_user["support_logs"] != "":
|
||||||
|
logger.info(
|
||||||
|
f"Found previous support log request at {exec_user['support_logs']}"
|
||||||
|
)
|
||||||
|
if self.helper.validate_traversal(
|
||||||
|
tempfile.gettempdir(), exec_user["support_logs"]
|
||||||
|
):
|
||||||
|
logger.debug("No transversal detected. Going for the delete.")
|
||||||
|
self.del_support_file(exec_user["support_logs"])
|
||||||
# pausing so on screen notifications can run for user
|
# pausing so on screen notifications can run for user
|
||||||
time.sleep(7)
|
time.sleep(7)
|
||||||
self.helper.websocket_helper.broadcast_user(
|
self.helper.websocket_helper.broadcast_user(
|
||||||
@ -177,6 +185,9 @@ class Controller:
|
|||||||
def del_support_file(self, temp_zip_storage):
|
def del_support_file(self, temp_zip_storage):
|
||||||
try:
|
try:
|
||||||
FileHelpers.del_file(temp_zip_storage)
|
FileHelpers.del_file(temp_zip_storage)
|
||||||
|
logger.info(
|
||||||
|
f"Old support logs successfully deleted from {temp_zip_storage}"
|
||||||
|
)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
logger.info("No temp file found. Assuming it's already been cleaned up")
|
logger.info("No temp file found. Assuming it's already been cleaned up")
|
||||||
except PermissionError:
|
except PermissionError:
|
||||||
|
@ -338,7 +338,7 @@ class CreateUser(ApiHandler):
|
|||||||
self.access_denied(user)
|
self.access_denied(user)
|
||||||
return
|
return
|
||||||
|
|
||||||
new_username = self.get_argument("username")
|
new_username = self.get_argument("username").lower()
|
||||||
new_pass = self.get_argument("password")
|
new_pass = self.get_argument("password")
|
||||||
|
|
||||||
if new_username:
|
if new_username:
|
||||||
|
@ -1223,8 +1223,6 @@ class PanelHandler(BaseHandler):
|
|||||||
|
|
||||||
elif page == "download_support_package":
|
elif page == "download_support_package":
|
||||||
temp_zip_storage = exec_user["support_logs"]
|
temp_zip_storage = exec_user["support_logs"]
|
||||||
# We'll reset the support path for this user now.
|
|
||||||
self.controller.users.set_support_path(exec_user["user_id"], "")
|
|
||||||
|
|
||||||
self.set_header("Content-Type", "application/octet-stream")
|
self.set_header("Content-Type", "application/octet-stream")
|
||||||
self.set_header(
|
self.set_header(
|
||||||
@ -1774,7 +1772,7 @@ class PanelHandler(BaseHandler):
|
|||||||
"system user is not editable"
|
"system user is not editable"
|
||||||
)
|
)
|
||||||
user_id = bleach.clean(self.get_argument("id", None))
|
user_id = bleach.clean(self.get_argument("id", None))
|
||||||
username = bleach.clean(self.get_argument("username", None))
|
username = bleach.clean(self.get_argument("username", None).lower())
|
||||||
password0 = bleach.clean(self.get_argument("password0", None))
|
password0 = bleach.clean(self.get_argument("password0", None))
|
||||||
password1 = bleach.clean(self.get_argument("password1", None))
|
password1 = bleach.clean(self.get_argument("password1", None))
|
||||||
email = bleach.clean(self.get_argument("email", "default@example.com"))
|
email = bleach.clean(self.get_argument("email", "default@example.com"))
|
||||||
@ -1943,7 +1941,7 @@ class PanelHandler(BaseHandler):
|
|||||||
self.finish()
|
self.finish()
|
||||||
|
|
||||||
elif page == "add_user":
|
elif page == "add_user":
|
||||||
username = bleach.clean(self.get_argument("username", None))
|
username = bleach.clean(self.get_argument("username", None).lower())
|
||||||
if username.lower() == "system":
|
if username.lower() == "system":
|
||||||
self.redirect(
|
self.redirect(
|
||||||
"/panel/error?error=Unauthorized access: "
|
"/panel/error?error=Unauthorized access: "
|
||||||
|
@ -46,7 +46,6 @@ class ApiServersServerActionHandler(BaseApiHandler):
|
|||||||
return Servers.select().where(Servers.server_name == name).exists()
|
return Servers.select().where(Servers.server_name == name).exists()
|
||||||
|
|
||||||
server_data = self.controller.servers.get_server_data_by_id(server_id)
|
server_data = self.controller.servers.get_server_data_by_id(server_id)
|
||||||
server_uuid = server_data.get("server_uuid")
|
|
||||||
new_server_name = server_data.get("server_name") + " (Copy)"
|
new_server_name = server_data.get("server_name") + " (Copy)"
|
||||||
|
|
||||||
name_counter = 1
|
name_counter = 1
|
||||||
|
@ -95,6 +95,7 @@ class ApiUsersIndexHandler(BaseApiHandler):
|
|||||||
)
|
)
|
||||||
|
|
||||||
username = data["username"]
|
username = data["username"]
|
||||||
|
username = str(username).lower()
|
||||||
password = data["password"]
|
password = data["password"]
|
||||||
email = data.get("email", "default@example.com")
|
email = data.get("email", "default@example.com")
|
||||||
enabled = data.get("enabled", True)
|
enabled = data.get("enabled", True)
|
||||||
|
@ -201,7 +201,6 @@ class ServerHandler(BaseHandler):
|
|||||||
server_data = self.controller.servers.get_server_data_by_id(
|
server_data = self.controller.servers.get_server_data_by_id(
|
||||||
server_id
|
server_id
|
||||||
)
|
)
|
||||||
server_uuid = server_data.get("server_uuid")
|
|
||||||
new_server_name = server_data.get("server_name") + " (Copy)"
|
new_server_name = server_data.get("server_name") + " (Copy)"
|
||||||
|
|
||||||
name_counter = 1
|
name_counter = 1
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"major": 4,
|
"major": 4,
|
||||||
"minor": 0,
|
"minor": 0,
|
||||||
"sub": 1,
|
"sub": 2,
|
||||||
"meta": "beta"
|
"meta": "beta"
|
||||||
}
|
}
|
||||||
|
@ -163,8 +163,29 @@
|
|||||||
<script type="text/javascript" src="/static/assets/js/motd.js"></script>
|
<script type="text/javascript" src="/static/assets/js/motd.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
/**
|
||||||
|
* Returns a random number between min (inclusive) and max (exclusive)
|
||||||
|
*/
|
||||||
|
function getRandomArbitrary(min, max) {
|
||||||
|
return Math.random() * (max - min) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a random integer between min (inclusive) and max (inclusive).
|
||||||
|
* The value is no lower than min (or the next integer greater than min
|
||||||
|
* if min isn't an integer) and no greater than max (or the next integer
|
||||||
|
* lower than max if max isn't an integer).
|
||||||
|
* Using Math.round() will give you a non-uniform distribution!
|
||||||
|
*/
|
||||||
|
function getRandomInt(min, max) {
|
||||||
|
min = Math.ceil(min);
|
||||||
|
max = Math.floor(max);
|
||||||
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$.extend($.fn.dataTable.defaults, {
|
$.extend($.fn.dataTable.defaults, {
|
||||||
language: {% raw translate('datatables', 'i18n', data['lang']) %}
|
// {{ '\nlanguage:' }} {% raw translate('datatables', 'i18n', data['lang']) %}
|
||||||
})
|
})
|
||||||
|
|
||||||
//used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security
|
//used to get cookies from browser - this is part of tornados xsrf protection - it's for extra security
|
||||||
@ -192,60 +213,93 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let usingWebSockets = false;
|
||||||
|
let webSocket = null;
|
||||||
// {% if request.protocol == 'https' %}
|
// {% if request.protocol == 'https' %}
|
||||||
let usingWebSockets = true;
|
usingWebSockets = true;
|
||||||
|
|
||||||
let listenEvents = [];
|
let listenEvents = [];
|
||||||
|
let wsOpen = false;
|
||||||
|
/**
|
||||||
|
* @type {number | null} reconnectorId An interval ID for the reconnector.
|
||||||
|
*/
|
||||||
|
let reconnectorId = null;
|
||||||
|
let failedConnectionCounter = 0; // https://stackoverflow.com/a/37038217/15388424
|
||||||
|
|
||||||
try {
|
const wsPageQueryParams = 'page_query_params=' + encodeURIComponent(location.search)
|
||||||
pageQueryParams = 'page_query_params=' + encodeURIComponent(location.search)
|
const wsPage = 'page=' + encodeURIComponent(location.pathname)
|
||||||
page = 'page=' + encodeURIComponent(location.pathname)
|
|
||||||
var wsInternal = new WebSocket('wss://' + location.host + '/ws?' + page + '&' + pageQueryParams);
|
|
||||||
wsInternal.onopen = function () {
|
|
||||||
console.log('opened WebSocket connection:', wsInternal)
|
|
||||||
};
|
|
||||||
wsInternal.onmessage = function (rawMessage) {
|
|
||||||
var message = JSON.parse(rawMessage.data);
|
|
||||||
|
|
||||||
console.log('got message: ', message)
|
const sendWssError = () => wsOpen || warn(
|
||||||
|
'WebSockets are required for Crafty to work. This websocket connection has been closed. Are you using a reverse proxy?',
|
||||||
|
'https://wiki.craftycontrol.com/en/4/docs/Reverse%20Proxy%20Examples',
|
||||||
|
'wssError'
|
||||||
|
)
|
||||||
|
|
||||||
listenEvents
|
function startWebSocket() {
|
||||||
.filter(listenedEvent => listenedEvent.event == message.event)
|
console.log('%c[Crafty Controller] %cConnecting the WebSocket', 'font-weight: 900; color: #800080;', 'font-weight: 900; color: #eee;');
|
||||||
.forEach(listenedEvent => listenedEvent.callback(message.data))
|
try {
|
||||||
};
|
var wsInternal = new WebSocket('wss://' + location.host + '/ws?' + wsPage + '&' + wsPageQueryParams);
|
||||||
wsInternal.onerror = function (errorEvent) {
|
wsInternal.onopen = function () {
|
||||||
console.error('WebSocket Error', errorEvent);
|
console.log('opened WebSocket connection:', wsInternal)
|
||||||
warn('WebSockets are required for Crafty to work. This websocket connection has been closed. Are you using a reverse proxy?', 'https://wiki.craftycontrol.com/en/4/docs/Reverse%20Proxy%20Examples')
|
wsOpen = true;
|
||||||
|
failedConnectionCounter = 0;
|
||||||
};
|
if (typeof reconnectorId === 'number') {
|
||||||
wsInternal.onclose = function (closeEvent) {
|
document.querySelectorAll('.wssError').forEach(el => el.remove())
|
||||||
console.log('Closed WebSocket', closeEvent);
|
clearInterval(reconnectorId);
|
||||||
setTimeout(sendWssError, 7000);
|
reconnectorId = null;
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
webSocket = {
|
|
||||||
on: function (event, callback) {
|
|
||||||
console.log('registered ' + event + ' event');
|
|
||||||
listenEvents.push({ event: event, callback: callback })
|
|
||||||
},
|
|
||||||
emit: function (event, data) {
|
|
||||||
var message = {
|
|
||||||
event: event,
|
|
||||||
data: data
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
wsInternal.onmessage = function (rawMessage) {
|
||||||
|
var message = JSON.parse(rawMessage.data);
|
||||||
|
|
||||||
wsInternal.send(JSON.stringify(message));
|
console.log('got message: ', message)
|
||||||
|
|
||||||
|
listenEvents
|
||||||
|
.filter(listenedEvent => listenedEvent.event == message.event)
|
||||||
|
.forEach(listenedEvent => listenedEvent.callback(message.data))
|
||||||
|
};
|
||||||
|
wsInternal.onerror = function (errorEvent) {
|
||||||
|
console.error('WebSocket Error', errorEvent);
|
||||||
|
};
|
||||||
|
wsInternal.onclose = function (closeEvent) {
|
||||||
|
wsOpen = false;
|
||||||
|
console.log('Closed WebSocket', closeEvent);
|
||||||
|
|
||||||
|
if (typeof reconnectorId !== 'number') {
|
||||||
|
setTimeout(sendWssError, 7000);
|
||||||
|
}
|
||||||
|
console.info("Reconnecting with a timeout of", (getRandomArbitrary(0, 2 ** failedConnectionCounter - 1) + 5) * 1000, "milliseconds");
|
||||||
|
// Discard old websocket and create a new one in 5 seconds
|
||||||
|
wsInternal = null
|
||||||
|
reconnectorId = setTimeout(startWebSocket, (getRandomArbitrary(0, 2 ** failedConnectionCounter - 1) + 5) * 1000)
|
||||||
|
|
||||||
|
failedConnectionCounter++;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
webSocket = {
|
||||||
|
on: function (event, callback) {
|
||||||
|
console.log('registered ' + event + ' event');
|
||||||
|
listenEvents.push({ event: event, callback: callback })
|
||||||
|
},
|
||||||
|
emit: function (event, data) {
|
||||||
|
var message = {
|
||||||
|
event: event,
|
||||||
|
data: data
|
||||||
|
}
|
||||||
|
|
||||||
|
wsInternal.send(JSON.stringify(message));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error while making websocket helpers', error);
|
||||||
|
usingWebSockets = false;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
console.error('Error while making websocket helpers', error);
|
|
||||||
usingWebSockets = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
startWebSocket();
|
||||||
// {% else %}
|
// {% else %}
|
||||||
let usingWebSockets = false;
|
|
||||||
warn('WebSockets are not supported in Crafty if not using the https protocol')
|
warn('WebSockets are not supported in Crafty if not using the https protocol')
|
||||||
var webSocket;
|
|
||||||
// {% end%}
|
// {% end%}
|
||||||
|
|
||||||
if (webSocket) {
|
if (webSocket) {
|
||||||
@ -265,8 +319,6 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
|
||||||
if (webSocket) {
|
|
||||||
webSocket.on('support_status_update', function (logs) {
|
webSocket.on('support_status_update', function (logs) {
|
||||||
if (logs.percent >= 100) {
|
if (logs.percent >= 100) {
|
||||||
document.getElementById('logs_progress_bar').innerHTML = '100%';
|
document.getElementById('logs_progress_bar').innerHTML = '100%';
|
||||||
@ -276,8 +328,6 @@
|
|||||||
document.getElementById('logs_progress_bar').style.width = logs.percent + '%';
|
document.getElementById('logs_progress_bar').style.width = logs.percent + '%';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
if (webSocket) {
|
|
||||||
webSocket.on('send_logs_bootbox', function (server_id) {
|
webSocket.on('send_logs_bootbox', function (server_id) {
|
||||||
var x = document.querySelector('.bootbox');
|
var x = document.querySelector('.bootbox');
|
||||||
if (x) {
|
if (x) {
|
||||||
@ -302,9 +352,6 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
if (webSocket) {
|
|
||||||
webSocket.on('send_eula_bootbox', function (server_id) {
|
webSocket.on('send_eula_bootbox', function (server_id) {
|
||||||
var x = document.querySelector('.bootbox');
|
var x = document.querySelector('.bootbox');
|
||||||
if (x) {
|
if (x) {
|
||||||
@ -339,10 +386,6 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendWssError(){
|
|
||||||
warn('WebSockets are required for Crafty to work. This websocket connection has been closed. Are you using a reverse proxy?', 'https://wiki.craftycontrol.com/en/4/docs/Reverse%20Proxy%20Examples')
|
|
||||||
}
|
|
||||||
|
|
||||||
function eulaAgree(server_id, command) {
|
function eulaAgree(server_id, command) {
|
||||||
//< !--this getCookie function is in base.html-- >
|
//< !--this getCookie function is in base.html-- >
|
||||||
var token = getCookie("_xsrf");
|
var token = getCookie("_xsrf");
|
||||||
@ -360,7 +403,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function warn(message, link = null) {
|
function warn(message, link = null, className = null) {
|
||||||
var closeEl = document.createElement('span');
|
var closeEl = document.createElement('span');
|
||||||
var strongEL = document.createElement('strong');
|
var strongEL = document.createElement('strong');
|
||||||
var msgEl = document.createElement('div');
|
var msgEl = document.createElement('div');
|
||||||
@ -397,6 +440,10 @@
|
|||||||
parentEl.appendChild(linkEl);
|
parentEl.appendChild(linkEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (className) {
|
||||||
|
parentEl.classList.add(className);
|
||||||
|
}
|
||||||
|
|
||||||
document.querySelector('.warnings').appendChild(parentEl);
|
document.querySelector('.warnings').appendChild(parentEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -835,7 +835,8 @@
|
|||||||
"ordering": false, // false to disable sorting (or any other option)
|
"ordering": false, // false to disable sorting (or any other option)
|
||||||
"paging": false
|
"paging": false
|
||||||
});
|
});
|
||||||
document.getElementById('first').setAttribute('draggable', false);
|
const first = document.querySelector('#first')
|
||||||
|
first !== null && first.setAttribute('draggable', false);
|
||||||
$('.dataTables_length').addClass('bs-select');
|
$('.dataTables_length').addClass('bs-select');
|
||||||
});
|
});
|
||||||
/* Search Bar End */
|
/* Search Bar End */
|
||||||
|
@ -211,29 +211,40 @@
|
|||||||
.tree-nested {
|
.tree-nested {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html, body, body > .container-scroller {
|
||||||
|
overflow: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editorManager {
|
||||||
|
top: 63px;
|
||||||
|
position: sticky;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<div class="col-md-6 col-sm-12">
|
<div class="col-md-6 col-sm-12">
|
||||||
<h2 id="fileError"></h2>
|
<div class="editorManager">
|
||||||
<div id="editorParent">
|
<h2 id="fileError"></h2>
|
||||||
{{ translate('serverFiles', 'editingFile', data['lang']) }} <span id="editingFile"></span>
|
<div id="editorParent">
|
||||||
<div id="editor" onresize="editor.resize()" style="resize: both;width: 100%;">file_contents</div>
|
{{ translate('serverFiles', 'editingFile', data['lang']) }} <span id="editingFile"></span>
|
||||||
<br />
|
<div id="editor" onresize="editor.resize()" style="resize: both;width: 100%;">file_contents</div>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
{{ translate('serverFiles', 'keybindings', data['lang']) }}:
|
||||||
|
<div class="btn-group" role="group">
|
||||||
|
<button onclick="setKeyboard(event.target)" class="btn btn-primary" data-handler-name="null">{{
|
||||||
|
translate('serverFiles', 'default', data['lang']) }}</button>
|
||||||
|
<button onclick="setKeyboard(event.target)" class="btn btn-secondary"
|
||||||
|
data-handler-name="ace/keyboard/vim">Vim</button>
|
||||||
|
<button onclick="setKeyboard(event.target)" class="btn btn-secondary"
|
||||||
|
data-handler-name="ace/keyboard/emacs">Emacs</button>
|
||||||
|
<button onclick="setKeyboard(event.target)" class="btn btn-secondary"
|
||||||
|
data-handler-name="ace/keyboard/sublime">Sublime</button>
|
||||||
|
</div>
|
||||||
|
<h3 id="file_warn"></h3>
|
||||||
|
<button class="btn btn-success" onclick="save()"><i class="fas fa-save"></i> {{ translate('serverFiles',
|
||||||
|
'save', data['lang']) }}</button>
|
||||||
|
<span style="color: #2fb689; margin-left: 10px;" id="save_status"></span>
|
||||||
</div>
|
</div>
|
||||||
{{ translate('serverFiles', 'keybindings', data['lang']) }}:
|
|
||||||
<div class="btn-group" role="group">
|
|
||||||
<button onclick="setKeyboard(event.target)" class="btn btn-primary" data-handler-name="null">{{
|
|
||||||
translate('serverFiles', 'default', data['lang']) }}</button>
|
|
||||||
<button onclick="setKeyboard(event.target)" class="btn btn-secondary"
|
|
||||||
data-handler-name="ace/keyboard/vim">Vim</button>
|
|
||||||
<button onclick="setKeyboard(event.target)" class="btn btn-secondary"
|
|
||||||
data-handler-name="ace/keyboard/emacs">Emacs</button>
|
|
||||||
<button onclick="setKeyboard(event.target)" class="btn btn-secondary"
|
|
||||||
data-handler-name="ace/keyboard/sublime">Sublime</button>
|
|
||||||
</div>
|
|
||||||
<h3 id="file_warn"></h3>
|
|
||||||
<button class="btn btn-success" onclick="save()"><i class="fas fa-save"></i> {{ translate('serverFiles',
|
|
||||||
'save', data['lang']) }}</button>
|
|
||||||
<span style="color: #2fb689; margin-left: 10px;" id="save_status"></span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -267,6 +278,17 @@
|
|||||||
let editor = ace.edit('editor');
|
let editor = ace.edit('editor');
|
||||||
editor.setTheme('ace/theme/dracula');
|
editor.setTheme('ace/theme/dracula');
|
||||||
editor.session.setUseSoftTabs(true);
|
editor.session.setUseSoftTabs(true);
|
||||||
|
editor.commands.addCommand({
|
||||||
|
name: 'saveFile',
|
||||||
|
bindKey: {
|
||||||
|
win: 'Ctrl-S',
|
||||||
|
mac: 'Command-S',
|
||||||
|
sender: 'editor|cli'
|
||||||
|
},
|
||||||
|
exec: function(env, args, request) {
|
||||||
|
save()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// mouseup = css resize end
|
// mouseup = css resize end
|
||||||
document.addEventListener("mouseup", function (e) {
|
document.addEventListener("mouseup", function (e) {
|
||||||
@ -364,8 +386,7 @@
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
var filePath = '';
|
let filePath = '', serverFileContent = '';
|
||||||
let file_loaded = false;
|
|
||||||
|
|
||||||
function clickOnFile(event) {
|
function clickOnFile(event) {
|
||||||
filePath = event.target.getAttribute('data-path');
|
filePath = event.target.getAttribute('data-path');
|
||||||
@ -386,7 +407,7 @@
|
|||||||
$('#fileError').toggle(false) // hide
|
$('#fileError').toggle(false) // hide
|
||||||
setFileName(event.target.innerText);
|
setFileName(event.target.innerText);
|
||||||
editor.session.setValue(json.content);
|
editor.session.setValue(json.content);
|
||||||
file_loaded = false;
|
serverFileContent = json.content;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -501,36 +522,14 @@
|
|||||||
timer = null;
|
timer = null;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
editor.on('change', function (event) {
|
/**
|
||||||
if (!event.ctrlKey && !event.shiftKey) {
|
* @param {boolean} saved
|
||||||
if (file_loaded) {
|
*/
|
||||||
document.getElementById('save_status').innerHTML = '<i class="fal fa-file"></i>';
|
const setSaveStatus = (saved) => {
|
||||||
document.getElementById('save_status').style.color = 'gray';
|
document.getElementById('save_status').innerHTML = `<i class="fal ${saved ? "fa-file-check" : "fa-file"}"></i>`;
|
||||||
} else {
|
document.getElementById('save_status').style.color = saved ? '#2fb689' : 'gray';
|
||||||
document.getElementById('save_status').innerHTML = '<i class="fal fa-file-check"></i>';
|
}
|
||||||
document.getElementById('save_status').style.color = '#2fb689';
|
['change', 'undo', 'redo'].forEach(event => editor.on(event, (event) => setSaveStatus(serverFileContent === editor.session.getValue())))
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
editor.on('undo', function (event) {
|
|
||||||
document.getElementById('save_status').innerHTML = '<i class="fal fa-file-xmark"></i>';
|
|
||||||
document.getElementById('save_status').style.color = 'gray';
|
|
||||||
|
|
||||||
});
|
|
||||||
editor.on('redo', function (event) {
|
|
||||||
document.getElementById('save_status').innerHTML = '<i class="fal fa-file-xmark"></i>';
|
|
||||||
document.getElementById('save_status').style.color = 'gray';
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#editor').bind('keydown', function (event) {
|
|
||||||
file_loaded = true;
|
|
||||||
if (event.ctrlKey && event.key === 's') {
|
|
||||||
event.preventDefault();
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
//console.log(event.keyCode);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
setFileName();
|
setFileName();
|
||||||
@ -576,11 +575,10 @@
|
|||||||
file_contents: text,
|
file_contents: text,
|
||||||
file_path: filePath
|
file_path: filePath
|
||||||
},
|
},
|
||||||
success: function (data) {
|
success: (data) => {
|
||||||
console.log("got response:");
|
serverFileContent = text;
|
||||||
document.getElementById("save_status").innerHTML = '<i class="fal fa-file-check"></i>';
|
setSaveStatus(true)
|
||||||
document.getElementById('save_status').style.color = '#2fb689';
|
}
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1097,7 +1095,13 @@
|
|||||||
function setKeyboard(target) {
|
function setKeyboard(target) {
|
||||||
var handlerName = target.getAttribute('data-handler-name');
|
var handlerName = target.getAttribute('data-handler-name');
|
||||||
if (handlerName == 'null') handlerName = null;
|
if (handlerName == 'null') handlerName = null;
|
||||||
editor.setKeyboardHandler(handlerName);
|
editor.setKeyboardHandler(handlerName, () => {
|
||||||
|
if (handlerName == 'ace/keyboard/vim') {
|
||||||
|
require("ace/keyboard/vim").Vim.defineEx('write', 'w', function() {
|
||||||
|
save();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
var nodes = target.parentNode.querySelectorAll("[data-handler-name]");
|
var nodes = target.parentNode.querySelectorAll("[data-handler-name]");
|
||||||
nodes.forEach(node => {
|
nodes.forEach(node => {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Check if config exists taking one from image if needed.
|
# Check if config exists taking one from image if needed.
|
||||||
if [ ! "$(ls -A --ignore=.gitkeep ./app/config)" ]; then
|
if [ ! "$(ls -A --ignore=.gitkeep ./app/config)" ]; then
|
||||||
echo "Wrapper | 🏗️ Config not found, pulling defaults..."
|
echo "\033[36mWrapper | \033[33m🏗️ Config not found, pulling defaults..."
|
||||||
mkdir ./app/config/ 2> /dev/null
|
mkdir ./app/config/ 2> /dev/null
|
||||||
cp -r ./app/config_original/* ./app/config/
|
cp -r ./app/config_original/* ./app/config/
|
||||||
|
|
||||||
@ -10,11 +10,24 @@ if [ ! "$(ls -A --ignore=.gitkeep ./app/config)" ]; then
|
|||||||
# We're running as root;
|
# We're running as root;
|
||||||
# Look for files & dirs that require group permissions to be fixed
|
# Look for files & dirs that require group permissions to be fixed
|
||||||
# This will do the full /crafty dir, so will take a miniute.
|
# This will do the full /crafty dir, so will take a miniute.
|
||||||
echo "Wrapper | 📋 Looking for problem bind mount permissions globally..."
|
echo "\033[36mWrapper | \033[35m📋 Looking for problem bind mount permissions globally..."
|
||||||
find . ! -group root -exec chgrp root {} \;
|
find . ! -group root -exec chgrp root {} \;
|
||||||
find . ! -perm g+rw -exec chmod g+rw {} \;
|
find . ! -perm g+rw -exec chmod g+rw {} \;
|
||||||
find . -type d ! -perm g+s -exec chmod g+s {} \;
|
find . -type d ! -perm g+s -exec chmod g+s {} \;
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
|
# Keep version file up to date with image
|
||||||
|
cp -f ./app/config_original/version.json ./app/config/version.json
|
||||||
|
|
||||||
|
# Compare if user's config is different from image, and show differences.
|
||||||
|
echo "\033[36mWrapper | \033[35m🏗️ Checking for config.json changes..."
|
||||||
|
cp -f ./app/config_original/config.json ./app/config/config_image_template
|
||||||
|
if [ "$(diff -q ./app/config/config.json ./app/config/config_image_template)" ]; then
|
||||||
|
echo "\033[36mWrapper | \033[33m👷 We've found differences in your local config, please review!,"
|
||||||
|
echo "\033[36m | \033[33m (This could be an outdated config.json)"
|
||||||
|
else
|
||||||
|
echo "\033[36mWrapper | \033[32m✅ Config good! Proceeding..."
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
@ -24,21 +37,21 @@ if [ $(id -u) -eq 0 ]; then
|
|||||||
# If we find files in import directory, we need to ensure all dirs are owned by the root group,
|
# If we find files in import directory, we need to ensure all dirs are owned by the root group,
|
||||||
# This fixes bind mounts that may have incorrect perms.
|
# This fixes bind mounts that may have incorrect perms.
|
||||||
if [ "$(ls -A --ignore=.gitkeep ./import)" ]; then
|
if [ "$(ls -A --ignore=.gitkeep ./import)" ]; then
|
||||||
echo "Wrapper | 📋 Files present in import, checking/fixing permissions..."
|
echo "\033[36mWrapper | \033[35m📋 Files present in import directory, checking/fixing permissions..."
|
||||||
echo "Wrapper | ⏳ Please be paitent for larger servers..."
|
echo "\033[36mWrapper | \033[33m⏳ Please be paitent for larger servers..."
|
||||||
find . ! -group root -exec chgrp root {} \;
|
find . ! -group root -exec chgrp root {} \;
|
||||||
find . ! -perm g+rw -exec chmod g+rw {} \;
|
find . ! -perm g+rw -exec chmod g+rw {} \;
|
||||||
find . -type d ! -perm g+s -exec chmod g+s {} \;
|
find . -type d ! -perm g+s -exec chmod g+s {} \;
|
||||||
echo "Wrapper | ✅ Permissions Fixed! (This will happen every boot until /import is empty!)"
|
echo "\033[36mWrapper | \033[32m✅ Permissions Fixed! (This will happen every boot until /import is empty!)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Switch user, activate our prepared venv and lauch crafty
|
# Switch user, activate our prepared venv and lauch crafty
|
||||||
args="$@"
|
args="$@"
|
||||||
echo "Wrapper | 🚀 Launching crafty with [$args]"
|
echo "\033[36mWrapper | \033[32m🚀 Launching crafty with [\033[34m$args\033[32m]"
|
||||||
exec sudo -u crafty bash -c "source ./.venv/bin/activate && exec python3 main.py $args"
|
exec sudo -u crafty bash -c "source ./.venv/bin/activate && exec python3 main.py $args"
|
||||||
else
|
else
|
||||||
# Activate our prepared venv
|
# Activate our prepared venv
|
||||||
echo "Wrapper | 🚀 Non-root host detected, using normal exec"
|
echo "\033[36mWrapper | \033[32m🚀 Non-root host detected, using normal exec"
|
||||||
. ./.venv/bin/activate
|
. ./.venv/bin/activate
|
||||||
# Use exec as our perms are already correct
|
# Use exec as our perms are already correct
|
||||||
# This is likely if using Kubernetes/OpenShift etc
|
# This is likely if using Kubernetes/OpenShift etc
|
||||||
|
Loading…
Reference in New Issue
Block a user