diff --git a/.gitlab/lint.yml b/.gitlab/lint.yml index 101e26b2..bc797808 100644 --- a/.gitlab/lint.yml +++ b/.gitlab/lint.yml @@ -44,13 +44,17 @@ black: # Code Climate/Quality Checking [https://pylint.pycqa.org/en/latest/] pylint: stage: lint - image: registry.gitlab.com/pipeline-components/pylint:0.21.1 + image: registry.gitlab.com/pipeline-components/pylint:latest tags: - docker rules: - if: "$CODE_QUALITY_DISABLED" when: never - if: "$CI_COMMIT_TAG || $CI_COMMIT_BRANCH" + before_script: + - apk update + - apk add gcc python3-dev linux-headers build-base + - pip3 install --no-cache-dir -r requirements.txt script: - pylint --exit-zero --load-plugins=pylint_gitlab --output-format=gitlab-codeclimate:codeclimate.json $(find -type f -name "*.py" ! -path "**/.venv/**" ! -path "**/app/migrations/**") artifacts: diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e2575fa..b6590c19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### New features - Use Papermc Group's API for `paper` & `folia` builds in server builder ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/688)) - Allow omission of player count from Dashboard (e.g. for proxy servers) ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/692)) +- Add lockout user for forgot password ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/694)) ### Refactor - Refactor subpage perm checks ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/695)) ### Bug fixes @@ -15,6 +16,7 @@ ### Tweaks - Refactor Forge server initialisation flow for newer versions ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/687)) - Remove scroll bars from player management ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/693)) +- Add warning to wizard for unsupported mc ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/701)) ### Lang - Update `zh_CN, pl_PL, nl_BE, lv_LV, he_IL, fr_FR, de_DE, lol_EN` translations for `4.2.3` ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/696)) - New `uk_UA, tr_TR, th_TH` translations for `4.2.3` ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/696)) diff --git a/app/classes/controllers/users_controller.py b/app/classes/controllers/users_controller.py index 87cc513c..5c6dd3d2 100644 --- a/app/classes/controllers/users_controller.py +++ b/app/classes/controllers/users_controller.py @@ -1,5 +1,9 @@ import logging import typing as t +import datetime +from datetime import timedelta +from zoneinfo import ZoneInfo +from apscheduler.schedulers.background import BackgroundScheduler from app.classes.models.servers import HelperServers from app.classes.models.users import HelperUsers @@ -8,6 +12,7 @@ from app.classes.models.crafty_permissions import ( PermissionsCrafty, EnumPermissionsCrafty, ) +from app.classes.shared.console import Console logger = logging.getLogger(__name__) @@ -22,6 +27,8 @@ class UsersController: self.helper = helper self.users_helper = users_helper self.authentication = authentication + self.scheduler = BackgroundScheduler(timezone="Etc/UTC") + self.scheduler.start() _permissions_props = { "name": { @@ -169,7 +176,8 @@ class UsersController: # create sets to store role data added_roles = set() removed_roles = set() - + if user_data.get("username", None) == "anti-lockout-user": + raise ValueError("Invalid Username") # search for changes in user data for key in user_data: if key == "user_id": @@ -245,6 +253,8 @@ class UsersController: superuser: bool = False, theme="default", ): + if username == "anti-lockout-user": + raise ValueError("Username is not valid") return self.users_helper.add_user( username, manager, @@ -353,3 +363,37 @@ class UsersController: def delete_user_api_key(self, key_id: str): return self.users_helper.delete_user_api_key(key_id) + + # ********************************************************************************** + # Lockout Methods + # ********************************************************************************** + def start_anti_lockout(self): + lockout_pass = self.helper.create_pass() + self.users_helper.add_user( + "anti-lockout-user", + None, + password=lockout_pass, + email="", + enabled=True, + superuser=True, + theme="anti-lockout", + ) + + Console.yellow( + f""" + Anti-lockout recovery account enabled! + {'/' * 74} + Username: anti-lockout-user + Password: {lockout_pass} + {'/' * 74}""" + ) + self.scheduler.add_job( + self.stop_anti_lockout, + "date", + id="anti-lockout-watcher", + run_date=datetime.datetime.now(ZoneInfo("Etc/UTC")) + timedelta(hours=1), + ) + + def stop_anti_lockout(self): + self.scheduler.remove_all_jobs() + self.users_helper.remove_user(self.get_id_by_name("anti-lockout-user")) diff --git a/app/classes/models/users.py b/app/classes/models/users.py index ccd8f1b0..e44d06fb 100644 --- a/app/classes/models/users.py +++ b/app/classes/models/users.py @@ -103,7 +103,9 @@ class HelperUsers: @staticmethod def get_all_users(): - query = Users.select().where(Users.username != "system") + query = Users.select().where( + Users.username != "system", Users.username != "anti-lockout-user" + ) return query @staticmethod diff --git a/app/classes/web/base_handler.py b/app/classes/web/base_handler.py index d8181b94..ced6cb97 100644 --- a/app/classes/web/base_handler.py +++ b/app/classes/web/base_handler.py @@ -12,6 +12,7 @@ from app.classes.shared.file_helpers import FileHelpers from app.classes.shared.main_controller import Controller from app.classes.shared.translation import Translation from app.classes.shared.main_models import DatabaseShortcuts +from app.classes.models.users import DoesNotExist logger = logging.getLogger(__name__) auth_log = logging.getLogger("auth") @@ -91,7 +92,10 @@ class BaseHandler(tornado.web.RequestHandler): t.Dict[str, t.Any]: The token's payload. t.Dict[str, t.Any]: The user's data from the database. """ - return self.controller.authentication.check(self.get_cookie("token")) + try: + return self.controller.authentication.check(self.get_cookie("token")) + except DoesNotExist: + return None def autobleach(self, name, text): for r in self.redactables: @@ -102,7 +106,7 @@ class BaseHandler(tornado.web.RequestHandler): if type(text) in self.nobleach: logger.debug("Auto-bleaching - bypass type") return text - return nh3.clean(text) + return nh3.clean(text) # pylint: disable=no-member def get_argument( self, diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index 02a5dbc1..a7e54974 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -80,6 +80,7 @@ class PanelHandler(BaseHandler): ) in self.controller.crafty_perms.list_defined_crafty_permissions(): argument = int( float( + # pylint: disable=no-member nh3.clean(self.get_argument(f"permission_{permission.name}", "0")) ) ) @@ -89,7 +90,10 @@ class PanelHandler(BaseHandler): ) q_argument = int( - float(nh3.clean(self.get_argument(f"quantity_{permission.name}", "0"))) + float( + # pylint: disable=no-member + nh3.clean(self.get_argument(f"quantity_{permission.name}", "0")) + ) ) if q_argument: server_quantity[permission.name] = q_argument @@ -302,6 +306,8 @@ class PanelHandler(BaseHandler): "Could not capture time zone from system. Falling back to Europe/London" ) tz = "Europe/London" + if exec_user["username"] == "anti-lockout-user": + page = "panel_config" page_data: t.Dict[str, t.Any] = { # todo: make this actually pull and compare version data @@ -501,7 +507,9 @@ class PanelHandler(BaseHandler): template = "panel/dashboard.html" elif page == "server_detail": + # pylint: disable=no-member subpage = nh3.clean(self.get_argument("subpage", "")) + # pylint: enable=no-member server_id = self.check_server_id() # load page the user was on last @@ -1360,7 +1368,9 @@ class PanelHandler(BaseHandler): template = "panel/panel_edit_user_apikeys.html" elif page == "remove_user": + # pylint: disable=no-member user_id = nh3.clean(self.get_argument("id", None)) + # pylint: enable=no-member if ( not superuser diff --git a/app/classes/web/public_handler.py b/app/classes/web/public_handler.py index 57e6ddd8..762d3fb1 100644 --- a/app/classes/web/public_handler.py +++ b/app/classes/web/public_handler.py @@ -29,8 +29,10 @@ class PublicHandler(BaseHandler): # self.clear_cookie("user_data") def get(self, page=None): + # pylint: disable=no-member error = nh3.clean(self.get_argument("error", "Invalid Login!")) error_msg = nh3.clean(self.get_argument("error_msg", "")) + # pylint: enable=no-member page_data = { "version": self.helper.get_version_string(), @@ -61,7 +63,11 @@ class PublicHandler(BaseHandler): template = "public/offline.html" elif page == "logout": + exec_user = self.get_current_user() self.clear_cookie("token") + # Delete anti-lockout-user on lockout...it's one time use + if exec_user[2]["username"] == "anti-lockout-user": + self.controller.users.stop_anti_lockout() # self.clear_cookie("user") # self.clear_cookie("user_data") self.redirect("/login") @@ -83,8 +89,10 @@ class PublicHandler(BaseHandler): ) def post(self, page=None): + # pylint: disable=no-member error = nh3.clean(self.get_argument("error", "Invalid Login!")) error_msg = nh3.clean(self.get_argument("error_msg", "")) + # pylint: enable=no-member page_data = { "version": self.helper.get_version_string(), @@ -104,10 +112,11 @@ class PublicHandler(BaseHandler): if self.request.query: next_page = "/login?" + self.request.query + # pylint: disable=no-member entered_username = nh3.clean(self.get_argument("username")) entered_password = self.get_argument("password") + # pylint: enable=no-member - # pylint: disable=no-member try: user_id = HelperUsers.get_user_id_by_name(entered_username.lower()) user_data = HelperUsers.get_user_model(user_id) diff --git a/app/classes/web/routes/api/api_handlers.py b/app/classes/web/routes/api/api_handlers.py index 706c346f..21c78c04 100644 --- a/app/classes/web/routes/api/api_handlers.py +++ b/app/classes/web/routes/api/api_handlers.py @@ -79,6 +79,7 @@ from app.classes.web.routes.api.crafty.stats.stats import ApiCraftyHostStatsHand from app.classes.web.routes.api.crafty.clogs.index import ApiCraftyLogIndexHandler from app.classes.web.routes.api.crafty.imports.index import ApiImportFilesIndexHandler from app.classes.web.routes.api.crafty.exe_cache import ApiCraftyJarCacheIndexHandler +from app.classes.web.routes.api.crafty.antilockout.index import ApiCraftyLockoutHandler def api_handlers(handler_args): @@ -94,6 +95,11 @@ def api_handlers(handler_args): ApiAuthInvalidateTokensHandler, handler_args, ), + ( + r"/api/v2/crafty/resetPass/?", + ApiCraftyLockoutHandler, + handler_args, + ), ( r"/api/v2/crafty/announcements/?", ApiAnnounceIndexHandler, diff --git a/app/classes/web/routes/api/crafty/antilockout/index.py b/app/classes/web/routes/api/crafty/antilockout/index.py new file mode 100644 index 00000000..0a9ab03a --- /dev/null +++ b/app/classes/web/routes/api/crafty/antilockout/index.py @@ -0,0 +1,24 @@ +import logging +from app.classes.web.base_api_handler import BaseApiHandler + +logger = logging.getLogger(__name__) + + +class ApiCraftyLockoutHandler(BaseApiHandler): + def get(self): + if self.controller.users.get_id_by_name("anti-lockout-user"): + return self.finish_json( + 425, {"status": "error", "data": "Lockout recovery already in progress"} + ) + self.controller.users.start_anti_lockout() + lockout_msg = ( + "Lockout account has been activated for 1 hour." + " Please find temporary credentials in the terminal" + ) + return self.finish_json( + 200, + { + "status": "ok", + "data": lockout_msg, + }, + ) diff --git a/app/frontend/static/assets/css/dark/style.css b/app/frontend/static/assets/css/dark/style.css index 12320636..cae93650 100755 --- a/app/frontend/static/assets/css/dark/style.css +++ b/app/frontend/static/assets/css/dark/style.css @@ -55,6 +55,49 @@ root, --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; } +:root.anti-lockout { + /*CHANGE THESE FOR THEMES*/ + --tooltip-bg: rgb(215, 82, 0); + --select-bg: #b8772c; + --ram-bg: #4d4d4e; + --base-text: white; + --outline: #c73929; + --card-banner-bg: #de7c26; + --deep-bg: #912f2f; + --dropdown-bg: #c83b3b; + /*END THEME VARIATION*/ + --blue: #00aeef; + --indigo: #6610f2; + --purple: #ab8ce4; + --pink: #E91E63; + --red: #ff0017; + --orange: #fb9678; + --yellow: #ffd500; + --green: #3bd949; + --teal: #58d8a3; + --cyan: #57c7d4; + --white: #ffffff; + --white-smoke: #f3f5f6; + --gray: #6c757d; + --gray-light: #8ba2b5; + --gray-lightest: #f7f7f9; + --primary: #dbc900; + --secondary: #dde4eb; + --success: #adff84; + --info: #dbc900; + --warning: #ffaf00; + --danger: #ff6258; + --light: #fbfbfb; + --dark: #252C46; + --breakpoint-xs: 0; + --breakpoint-sm: 576px; + --breakpoint-md: 768px; + --breakpoint-lg: 992px; + --breakpoint-xl: 1200px; + --font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; +} + :root.light { /*CHANGE THESE FOR THEMES*/ --tooltip-bg: white; @@ -322,7 +365,7 @@ sup { } a { - color: #007bff; + color: var(--primary); text-decoration: none; background-color: transparent; } diff --git a/app/frontend/templates/base.html b/app/frontend/templates/base.html index 48c6ee95..8d72ece6 100755 --- a/app/frontend/templates/base.html +++ b/app/frontend/templates/base.html @@ -1,5 +1,5 @@ - + @@ -256,8 +256,9 @@ const sendWssError = () => wsOpen || warn( 'WebSockets are required for Crafty to work. This websocket connection has been closed. Are you using a reverse proxy?', - 'https://docs.craftycontrol.com/pages/getting-started/proxies/', - 'wssError' + link='https://docs.craftycontrol.com/pages/getting-started/proxies/', + link_msg="See our documentation for details", + className='wssError' ) function startWebSocket() { @@ -459,7 +460,7 @@ } - function warn(message, link = null, className = null) { + function warn(message, link = null, link_msg=null, className = null, bg_color="#f7970f") { var closeEl = document.createElement('span'); var strongEL = document.createElement('strong'); var msgEl = document.createElement('div'); @@ -481,14 +482,14 @@ var parentEl = document.createElement('div'); parentEl.style.padding = '20px'; - parentEl.style.backgroundColor = '#f7970f'; + parentEl.style.backgroundColor = bg_color; parentEl.appendChild(closeEl); parentEl.appendChild(msgEl); if (link) { let linkEl = document.createElement('a') linkEl.href = link; - linkEl.innerHTML = "See our documentation for details."; + linkEl.innerHTML = link_msg; linkEl.style.color = 'white'; linkEl.style.textDecoration = 'underline'; linkEl.target = "_blank"; @@ -580,6 +581,15 @@ $(document).ready(function () { console.log('%c[Crafty Controller] %cReady for JS!', 'font-weight: 900; color: #800080;', 'font-weight: 900; color: #eee;'); + if ($(document.documentElement).data("username") === "anti-lockout-user"){ + warn( + '⚠️You are in a recovery account. Access is limited!', + link='/logout', + link_msg="Click here to log out after you change your password. ⚠️", + className='anti-lockout', + bg_color='#6887dc' + ) + } $('#support_logs').click(function () { var dialog = bootbox.dialog({ message: "

{{ translate('notify', 'preparingLogs', data['lang']) }}

", diff --git a/app/frontend/templates/panel/panel_config.html b/app/frontend/templates/panel/panel_config.html index 5e9623b1..fee5c65d 100644 --- a/app/frontend/templates/panel/panel_config.html +++ b/app/frontend/templates/panel/panel_config.html @@ -281,7 +281,7 @@ \ No newline at end of file diff --git a/app/frontend/templates/server/wizard.html b/app/frontend/templates/server/wizard.html index bb5fc175..2d84e6aa 100644 --- a/app/frontend/templates/server/wizard.html +++ b/app/frontend/templates/server/wizard.html @@ -107,7 +107,8 @@ - +
@@ -1233,6 +1234,20 @@ } }) } + $("#server").change(function (){ + let selected_version = $("#server :selected").text().split("."); + if(parseInt(selected_version[0]) === 1 && parseInt(selected_version[1]) < 8 ){ + $('[data-toggle="popover"]').popover(); + if ($(window).width() < 1000) { + $('.version-hint').attr("data-placement", "top") + } else { + $('.version-hint').attr("data-placement", "right") + } + $('.version-hint').popover("show"); + }else{ + $('.version-hint').popover("hide"); + } + }); function serverJarChange(selectObj) { const type_select = document.getElementById('server_jar') diff --git a/app/translations/de_DE.json b/app/translations/de_DE.json index 1a7f8995..22bf6a57 100644 --- a/app/translations/de_DE.json +++ b/app/translations/de_DE.json @@ -579,6 +579,7 @@ "serverUpload": "Als Zip-Datei Gepackten Server hochladen", "serverVersion": "Server Version", "sizeInGB": "Größe in GB", + "unsupported": "Minecraft-Versionen vor 1.8 sind nicht vollständig auf Kompatibilität getestet. Bei einer Installation verhalten diese sich möglicherweise nicht wie erwartet.", "uploadButton": "Hochladen", "uploadZip": "Zip-Datei für Serverimport hochladen", "zipPath": "Server Pfad" diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index 32721082..06a37172 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -578,6 +578,7 @@ "serverUpload": "Upload Zipped Server", "serverVersion": "Server Version", "sizeInGB": "Size in GB", + "unsupported": "Minecraft versions lower than 1.8 are not supported by Crafty. You may still install it. Results will vary.", "uploadButton": "Upload", "uploadZip": "Upload Zip File For Server Import", "zipPath": "Server Path" diff --git a/app/translations/fr_FR.json b/app/translations/fr_FR.json index aa45a69b..4a0b88b1 100644 --- a/app/translations/fr_FR.json +++ b/app/translations/fr_FR.json @@ -579,6 +579,7 @@ "serverUpload": "Charger le fichier Zippé", "serverVersion": "Version du Serveur", "sizeInGB": "Taille en GB", + "unsupported": "Les versions Minecraft inférieures à 1.8 ne sont pas supportées par Crafty. Elles peuvent être installées, mais leur fonctionnement peut varier.", "uploadButton": "Chargement", "uploadZip": "Charger le fichier pour l'importation du Serveur", "zipPath": "Chemin du Serveur" diff --git a/app/translations/he_IL.json b/app/translations/he_IL.json index cff35559..3b6fa3ec 100644 --- a/app/translations/he_IL.json +++ b/app/translations/he_IL.json @@ -579,6 +579,7 @@ "serverUpload": "העלה שרת מכווץ", "serverVersion": "גרסת השרת", "sizeInGB": "גודל ב-GB", + "unsupported": "גרסאות מיינקראפט מתחת ל1.8 לא נתמכות על ידי קראפטי. אפשר להתקין אותם אבל הביצועים לא יהיו עקביים.", "uploadButton": "העלה", "uploadZip": "העלה קובץ Zip לייבוא שרת", "zipPath": "נתיב שרת" diff --git a/app/translations/lol_EN.json b/app/translations/lol_EN.json index 4836501b..8e51e4cb 100644 --- a/app/translations/lol_EN.json +++ b/app/translations/lol_EN.json @@ -579,6 +579,7 @@ "serverUpload": "UPLOADZ ZIPPED SERVR", "serverVersion": "SERVR VERZHUN", "sizeInGB": "SIEZ IN GIGABITEZ", + "unsupported": "MINECRAFTZ OLDR THAN 1.8 NOT FRENDS WIF CWAFTY. U CAN STILL TRY INSTALLIN'. IT MIGHT WORKS, MIGHT NOT.", "uploadButton": "UPLOADZ", "uploadZip": "UPLOADZ ZIP FISH FOR SERVR BRING IN", "zipPath": "WER IZ ZIP FISH (PETH)" diff --git a/app/translations/lv_LV.json b/app/translations/lv_LV.json index 354a0e54..09dec926 100644 --- a/app/translations/lv_LV.json +++ b/app/translations/lv_LV.json @@ -580,6 +580,7 @@ "serverUpload": "Augšupielādē Saspiestu (Zipotu) Serveri", "serverVersion": "Servera Versija", "sizeInGB": "lielums, GB", + "unsupported": "Minecraft versijas zem 1.8 netiek atbalstītas iekš Crafty. Jūs tāpat variet tās instalēt, bet rezultāti atškirsies.", "uploadButton": "Augšupielādēt", "uploadZip": "Augšupielādē Zip Failu Priekš Serveru Importa", "zipPath": "Servera Ceļš" diff --git a/app/translations/nl_BE.json b/app/translations/nl_BE.json index c9414bbe..f81f8bf5 100644 --- a/app/translations/nl_BE.json +++ b/app/translations/nl_BE.json @@ -579,6 +579,7 @@ "serverUpload": "Gezipt bestand uploaden", "serverVersion": "Serverversie", "sizeInGB": "Grootte in GB", + "unsupported": "Minecraft-versies lager dan 1.8 worden niet ondersteund door Crafty. Je kunt het nog steeds installeren. De resultaten kunnen variëren.", "uploadButton": "Uploaden", "uploadZip": "Upload zip-bestand voor serverimport", "zipPath": "Serverpad" diff --git a/app/translations/pl_PL.json b/app/translations/pl_PL.json index 86f8f9cb..e2a2d203 100644 --- a/app/translations/pl_PL.json +++ b/app/translations/pl_PL.json @@ -578,6 +578,7 @@ "serverUpload": "Wgraj ZIP Serwera", "serverVersion": "Wersja serwera", "sizeInGB": "Wielkość w GB", + "unsupported": "Wersje Minecrafta poniżej 1.8 nie są wspierane przez Crafty. Mimo to możesz je zainstalować - jednakże nie jesteśmy w stanie zagwarantować ich działanie.", "uploadButton": "Wgraj", "uploadZip": "Wgraj plik Zip dla imprtowania serwera", "zipPath": "Server Path" diff --git a/app/translations/th_TH.json b/app/translations/th_TH.json index a833cf1c..ce377492 100644 --- a/app/translations/th_TH.json +++ b/app/translations/th_TH.json @@ -578,6 +578,7 @@ "serverUpload": "อัปโหลดเซิร์ฟเวอร์ซิป", "serverVersion": "เวอร์ชันเซิร์ฟเวอร์", "sizeInGB": "ขนาดเป็นกิกะไบต์", + "unsupported": "Crafty ไม่รองรับ Minecraft เวอร์ชั่นต่ำกว่า 1.8 คุณยังสามารถติดตั้งได้ ผลลัพธ์จะแตกต่างกันออกไป", "uploadButton": "อัปโหลด", "uploadZip": "อัปโหลดไฟล์ซิปสำหรับการนำเข้าเซิร์ฟเวอร์", "zipPath": "เส้นทางเซิร์ฟเวอร์" diff --git a/app/translations/tr_TR.json b/app/translations/tr_TR.json index 9ab3a6c8..cf96f2b3 100644 --- a/app/translations/tr_TR.json +++ b/app/translations/tr_TR.json @@ -578,6 +578,7 @@ "serverUpload": "Sıkıştırılmış Sunucu Yükle", "serverVersion": "Sunucu Sürümü", "sizeInGB": "GB cinsinden boyut", + "unsupported": "Minecraft'ın 1.8'den eski sürümleri Crafty tarafından desteklenmemektedir. Yine de yükleyebilirsiniz. Sonuçlar değişkenlik gösterecektir.", "uploadButton": "Dosya Yükle", "uploadZip": "Sunucu İçe Aktarımı İçin Zip Dosyası Yükle", "zipPath": "Sunucu Konumu" diff --git a/app/translations/uk_UA.json b/app/translations/uk_UA.json index a7018a5f..a22c1de9 100644 --- a/app/translations/uk_UA.json +++ b/app/translations/uk_UA.json @@ -578,6 +578,7 @@ "serverUpload": "Вивантажити архівований сервер", "serverVersion": "Версія сервера", "sizeInGB": "Об'єм пам'яті в ГБ", + "unsupported": "Minecraft версії старіші ніж 1.8, не підтримуються Crafty. Ви все ще можете встановити їх, але результат буде непередбачуваний.", "uploadButton": "Вивантажити", "uploadZip": "Вивантажити архів(.zip) для імпорту сервера", "zipPath": "Шлях архіву сервера" diff --git a/app/translations/zh_CN.json b/app/translations/zh_CN.json index ffac7b3b..4ffa844b 100644 --- a/app/translations/zh_CN.json +++ b/app/translations/zh_CN.json @@ -579,6 +579,7 @@ "serverUpload": "上传打包为 Zip 文件的服务器", "serverVersion": "服务器版本", "sizeInGB": "大小(以 GB 为单位)", + "unsupported": "Crafty 不支持低于 1.8 的 Minecraft 版本。你仍可以安装,但结果无法保证。", "uploadButton": "上传", "uploadZip": "上传 Zip 文件以导入服务器", "zipPath": "服务器路径" diff --git a/main.py b/main.py index 75997247..ebaf7806 100644 --- a/main.py +++ b/main.py @@ -388,6 +388,8 @@ if __name__ == "__main__": # Master config.json in helpers.py Console.info("Checking for remote changes to config.json") controller.get_config_diff() + # Delete anti-lockout-user + controller.users.stop_anti_lockout() Console.info("Remote change complete.") # startup the web server