Compare commits

...

6 Commits

Author SHA1 Message Date
Andrew
6f2a366b24 Merge branch 'refactor/server-files' into 'dev'
Draft: Rip + Replace Files Frontend

See merge request crafty-controller/crafty-4!909
2025-12-03 14:47:01 -05:00
=
8280916bc3 Merge branch 'dev' into refactor/server-files 2025-12-03 14:45:51 -05:00
=
8c46d2a8ef Check if file is modified on server before saving 2025-12-03 11:53:17 -05:00
Iain Powrie
a34206b652 Merge branch 'weblate-crafty-controller-crafty-controller' into 'dev'
Translations update from Weblate

See merge request crafty-controller/crafty-4!923
2025-11-30 11:14:31 +00:00
Weblate
b2de3143e0 Merge remote-tracking branch 'origin/dev' into dev 2025-11-29 20:14:44 +00:00
Kanatsanan
e3058cb3b5 Translated (Thai)
Currently translated at 100.0% (717 of 717 strings)

Translation: Crafty Controller/Crafty Controller
Translate-URL: https://translate.arcadiatech.org/projects/crafty-controller/crafty-controller/th/
2025-11-29 20:14:43 +00:00
5 changed files with 126 additions and 38 deletions

View File

@@ -55,7 +55,18 @@ files_patch_schema = {
"error": "typeString",
"fill": True,
},
"modified_epoch": {
"type": "number",
"error": "typeEpoch",
"fill": True,
},
"overwrite": {
"type": "boolean",
"error": "typeBoolean",
"fill": True,
},
},
"required": ["path", "contents"],
"additionalProperties": False,
"minProperties": 1,
}
@@ -357,9 +368,8 @@ class ApiServersServerFilesIndexHandler(BaseApiHandler):
can_open, mime = self.file_helper.probably_can_open_file(
data["path"]
)
modified_time = datetime.fromtimestamp(
Path(data["path"]).stat().st_mtime
)
modified_epoch = Path(data["path"]).stat().st_mtime
modified_time = datetime.fromtimestamp(modified_epoch)
try:
file_size = os.path.getsize(data["path"])
except (OSError, IOError):
@@ -368,6 +378,7 @@ class ApiServersServerFilesIndexHandler(BaseApiHandler):
"mime": mime,
"modified": modified_time.strftime(HUMAN_TIME_FORMAT),
"size": Helpers.human_readable_file_size(file_size),
"modified_epoch": modified_epoch,
}
with open(data["path"], encoding="utf-8") as file:
file_contents = file.read()
@@ -571,10 +582,31 @@ class ApiServersServerFilesIndexHandler(BaseApiHandler):
)
file_path = Helpers.get_os_understandable_path(data["path"])
file_contents = data["contents"]
if Path(data["path"]).stat().st_mtime > data.get(
"modified_epoch", 1.5
) and not data.get("overwrite"):
self.set_status(409)
return self.finish()
# Open the file in write mode and store the content in file_object
with open(file_path, "w", encoding="utf-8") as file_object:
file_object.write(file_contents)
return self.finish_json(200, {"status": "ok"})
# Update file details
modified_epoch = Path(data["path"]).stat().st_mtime
modified_time = datetime.fromtimestamp(modified_epoch)
try:
file_size = os.path.getsize(data["path"])
except (OSError, IOError):
file_size = 0
attributes = {
"mime": self.file_helper.check_mime_types(data["path"]),
"modified": modified_time.strftime(HUMAN_TIME_FORMAT),
"size": Helpers.human_readable_file_size(file_size),
"modified_epoch": modified_epoch,
}
return self.finish_json(
200, {"status": "ok", "data": {"attributes": attributes}}
)
def put(self, server_id: str, _backup_id):
auth_data = self.authenticate_user()

View File

@@ -1,6 +1,7 @@
const urlParams = new URLSearchParams(globalThis.location.search);
const serverId = urlParams.get("server_id");
const path = decodeURIComponent(urlParams.get("file"))
let modified_epoch = 1.5;
let serverFileContent = "";
let themes = { "dark": "ace/theme/monokai", "light": "ace/theme/chrome", "default": "ace/theme/dracula" }
let theme = themes["default"];
@@ -160,6 +161,7 @@ async function get_file() {
$("#editorParent").toggle(true); // show
editor.session.setValue(responseData.data.content);
serverFileContent = responseData.data.content;
modified_epoch = responseData.data.attributes.modified_epoch;
setSaveStatus(true);
$("#file_size_sm").text(responseData.data.attributes.size);
$("#file_type_sm").text(responseData.data.attributes.mime);
@@ -247,7 +249,7 @@ for (let ev of event_types) {
)
}
async function save() {
async function save(overwrite = false) {
let text = editor.session.getValue();
const token = getCookie("_xsrf");
@@ -256,12 +258,41 @@ async function save() {
headers: {
"X-XSRFToken": token,
},
body: JSON.stringify({ path: path, contents: text }),
body: JSON.stringify({ path: path, contents: text, modified_epoch: modified_epoch, overwrite: overwrite }),
});
if (res.status === 409) {
bootbox.prompt({
title: `${$("#editor-wrapper").attr("data-changeConflict")}`,
message: `${$("#editor-wrapper").attr("data-serverModified")}`,
inputType: 'select',
inputOptions: [{
text: `${$("#editor-wrapper").attr("data-overwrite")}`,
value: 'overwrite'
},
{
text: `${$("#editor-wrapper").attr("data-repull")}`,
value: 'repull'
},],
callback: function (result) {
if (result === "overwrite") {
save(true);
} else if (result === "repull") {
get_file();
} else {
return;
}
}
});
}
let responseData = await res.json();
if (responseData.status === "ok") {
serverFileContent = text;
modified_epoch = responseData.data.attributes.modified_epoch;
setSaveStatus(true);
$("#file_size_sm").text(responseData.data.attributes.size);
$("#file_type_sm").text(responseData.data.attributes.mime);
$("#file_modified_sm").text(responseData.data.attributes.modified);
} else {
bootbox.alert({
title: responseData.error,

View File

@@ -4,7 +4,12 @@ end %} {% block content %}
<link href="/static/assets/css/partial/crafty-editor.css" rel="stylesheet">
<div class="content-wrapper">
<div class="content-wrapper" id="editor-wrapper"
data-changeConflict='{{translate("serverFiles", "changeConflict", data["lang"])}}'
data-serverModified='{{translate("serverFiles", "serverModified", data["lang"])}}'
data-cancel='{{translate("serverFiles", "cancel", data["lang"])}}'
data-overwrite='{{translate("serverFiles", "overwrite", data["lang"])}}'
data-repull='{{translate("serverFiles", "repull", data["lang"])}}'>
<!-- Page Title Header Starts-->
<div class="row page-title-header centered">
<div id="server-name" class="info-flex">

View File

@@ -515,6 +515,7 @@
},
"serverFiles": {
"cancel": "Cancel",
"changeConflict": "Server State Conflict",
"clickUpload": "Click here to select your files",
"close": "Close",
"copy": "Copy",
@@ -543,9 +544,12 @@
"noscript": "The file manager does not work without JavaScript",
"operationStatus": "File Operation Status",
"options": "Options",
"overwrite": "Overwrite changes on server",
"rename": "Rename",
"renameItemQuestion": "What should the new name be?",
"repull": "Load up to date file from server (All local changes will be lost)",
"save": "Save",
"serverModified": "The state of this file has changed on the server since you loaded this page. Please choose an option.",
"size": "Size",
"type": "Type",
"unsupportedLanguage": "Warning: This is not a supported file type",

View File

@@ -1,6 +1,6 @@
{
"404": {
"contact": "ติดต่อฝ่ายสนับสนุน Crafty Control ผ่านดิสคอร์ด",
"contact": "ติดต่อฝ่ายสนับสนุน Crafty Control ผ่าน Discord",
"notFound": "ไม่พบหน้าเว็บ",
"unableToFind": "เราไม่พบหน้าที่คุณกำลังมองหา โปรดลองอีกครั้งหรือย้อนกลับและโหลดหน้าเว็บใหม่อีกครั้ง"
},
@@ -32,7 +32,9 @@
"yes": "ใช่"
},
"base": {
"doesNotWorkWithoutJavascript": "<strong>คำเตือน: </strong>Crafty จะทำงานไม่ถูกต้องเมื่อไม่ได้เปิดใช้งาน JavaScript!"
"doesNotWorkWithoutJavascript": "<strong>คำเตือน: </strong>Crafty จะทำงานไม่ถูกต้องเมื่อไม่ได้เปิดใช้งาน JavaScript!",
"createMFA": "เพิ่มการยืนยันตัวตนแบบหลายปัจจัย (MFA) ให้บัญชีของคุณ!",
"getMFA": "รักษาความปลอดภัยของเซิร์ฟเวอร์ของคุณ!"
},
"credits": {
"developmentTeam": "ทีมพัฒนา",
@@ -87,7 +89,7 @@
"cpuUsage": "การใช้งาน CPU",
"crashed": "ล้มเหลว",
"dashboard": "แผงควบคุม",
"delay-explained": "บริการ/ตัวแทนเพิ่งเริ่มต้นและกำลังชะลอการเริ่มต้นเซิร์ฟเวอร์ Minecraft",
"delay-explained": "ระบบพิ่งเริ่มต้น ทำให้การเปิดเซิร์ฟเวอร์ Minecraft ล่าช้าลง",
"host": "โฮส",
"installing": "กำลังติดตั้ง...",
"kill": "บังคับหยุดการทำงาน",
@@ -196,12 +198,12 @@
"eulaTitle": "เห็นด้วยกับ EULA",
"fileError": "ประเภทไฟล์ต้องเป็นรูปภาพ",
"hereIsTheError": "นี่คือข้อผิดพลาด",
"installerJava": "ไม่สามารถติดตั้ง {} : การติดตั้ง Forge Server ต้องใช้ Java เราตรวจพบว่าคุณไม่ได้ติดตั้ง Java กรุณาติดตั้ง Java จากนั้นติดตั้งเซิร์ฟเวอร์อีกครั้ง",
"installerJava": "ติดตั้ง {} ล้มเหลว: การติดตั้งเซิร์ฟเวอร์ Forge ต้องใช้ Java เราตรวจพบว่าไม่ได้ติดตั้ง Java กรุณาติดตั้ง Java จากนั้นจึงทำการติดตั้งเซิร์ฟเวอร์",
"internet": "เราตรวจพบว่าเครื่องที่ใช้งาน Crafty ไม่มีการเชื่อมต่ออินเทอร์เน็ต การเชื่อมต่อผู้ใช้กับเซิร์ฟเวอร์อาจถูกจำกัด",
"migration": "พื้นที่เก็บข้อมูลเซิร์ฟเวอร์หลักของ Crafty กำลังถูกย้ายไปยังตำแหน่งใหม่ การเริ่มต้นเซิร์ฟเวอร์ทั้งหมดถูกระงับในช่วงเวลานี้ โปรดรอในขณะที่เราย้ายข้อมูลนี้ให้เสร็จสิ้น",
"no-file": "ดูเหมือนเราจะไม่พบไฟล์ที่ร้องขอ ตรวจสอบเส้นทางอีกครั้ง, Crafty เข้าถึงสิทธิ์ที่เหมาะสมหรือไม่?",
"migration": "พื้นที่เก็บข้อมูลเซิร์ฟเวอร์หลักของ Crafty กำลังถูกย้ายไปยังตำแหน่งใหม่ การเริ่มต้นเซิร์ฟเวอร์ทั้งหมดถูกระงับไว้ในช่วงเวลานี้ โปรดรอในขณะที่เราดำเนินการย้ายข้อมูลนี้ให้เสร็จสิ้น",
"no-file": "เราไม่สามารถค้นหาไฟล์ที่ร้องขอได้ โปรดตรวจสอบเส้นทางอีกครั้ง Crafty มีสิทธิ์การเข้าถึงที่เหมาะสมหรือไม่?",
"noInternet": "Crafty ประสบปัญหาในการเข้าถึงอินเทอร์เน็ต การสร้างเซิร์ฟเวอร์ถูกปิดใช้งาน โปรดตรวจสอบการเชื่อมต่ออินเทอร์เน็ตของคุณและโหลดหน้านี้ใหม่อีกครั้ง",
"noJava": "เซิร์ฟเวอร์ {} ไม่สามารถเริ่มต้นได้เนื่องจากรหัสข้อผิดพลาด: เราตรวจพบว่าคุณไม่ได้ติดตั้ง Java กรุณาติดตั้ง Java จากนั้นเริ่มเซิร์ฟเวอร์ใหม่อีกครั้ง",
"noJava": "เซิร์ฟเวอร์ {} เริ่มต้นล้มเหลวเนื่องจากข้อผิดพลาด: เราตรวจพบว่าไม่ได้ติดตั้ง Java กรุณาติดตั้ง Java จากนั้นจึงเริ่มต้นเซิร์ฟเวอร์",
"not-downloaded": "ดูเหมือนว่าเราจะไม่พบแฟ้มกระทำการของคุณ (.jar) ตรวจสอบให้แน่ใจว่าการดาวโหลดน์เสร็จสิ้นแล้ว, การอนุญาตถูกตั้งไปยังแฟ้มกระทำการหรือไม่?",
"portReminder": "เราตรวจพบว่านี่เป็นครั้งแรกที่มีการเรียกใช้ {} ตรวจสอบให้แน่ใจว่าได้ Forward port {} ผ่านเราเตอร์/ไฟร์วอลล์ของคุณเพื่อให้สามารถเข้าถึงได้จากอินเทอร์เน็ตจากระยะไกล",
"privMsg": "และ ",
@@ -234,7 +236,8 @@
"cancel": "ยกเลิก",
"cooldown": "กำลังใช้งานการคูลดาวน์ ลองอีกครั้งใน",
"totpSelect": "แอปตรวจสอบสิทธิ์",
"passwordRecovery": "กู้คืนรหัสผ่าน"
"passwordRecovery": "กู้คืนรหัสผ่าน",
"accountDisabled": "บัญชีผู้ใช้ของคุณถูกปิดใช้งานโดยผู้ดูแลระบบ โปรดติดต่อผู้ดูแลระบบของคุณสำหรับรายละเอียดเพิ่มเติม"
},
"notify": {
"activityLog": "บันทึกกิจกรรม",
@@ -245,8 +248,8 @@
"logout": "ออกจากระบบ",
"preparingLogs": " โปรดรอสักครู่ในขณะที่เราเตรียมบันทึกของคุณ... เราจะส่งการแจ้งเตือนเมื่อเสร็จสิ้น การดำเนินการนี้อาจใช้เวลาสักครู่สำหรับการเตรียมข้อมูลขนาดใหญ่",
"supportLogs": "บันทึกการสนับสนุน",
"schedule_desc": "เราตรวจพบว่างานตามกำหนดการบางส่วนหรือทั้งหมดของคุณโอนย้ายไม่สำเร็จในระหว่างการอัปเกรด โปรดยืนยันกำหนดการของคุณในแท็บกำหนดการ",
"backup_desc": "เราตรวจพบว่าการย้ายข้อมูลสำรองอาจล้มเหลวบางส่วนหรือทั้งหมด โปรดยืนยันข้อมูลสำรองของคุณในหน้าข้อมูลสำรอง",
"schedule_desc": "เราตรวจพบว่างานตามกำหนดการบางส่วนหรือทั้งหมดของคุณโอนย้ายไม่สำเร็จในระหว่างการอัปเกรด โปรดตรวจสอบกำหนดการของคุณที่แท็บกำหนดการ",
"backup_desc": "เราตรวจพบว่าการย้ายข้อมูลสำรองอาจล้มเหลวบางส่วนหรือทั้งหมด โปรดตรวจสอบบันทึกข้อมูลสำรองของคุณที่แท็บข้อมูลสำรอง",
"backup_title": "คำเตือนการโยกย้ายข้อมูลสำรอง",
"schedule_title": "คำเตือนการโยกย้ายกำหนดการ",
"accountSettings": "การตั้งค่าบัญชี"
@@ -309,7 +312,8 @@
"selectManager": "เลือกผู้จัดการสำหรับบทบาทนี้",
"serverAccess": "ให้สิทธิ์การเข้าถึง?",
"serverName": "ชื่อเซิร์ฟเวอร์",
"serversDesc": "เซิร์ฟเวอร์ที่บทบาทนี้ได้รับอนุญาตให้เข้าถึง"
"serversDesc": "เซิร์ฟเวอร์ที่บทบาทนี้ได้รับอนุญาตให้เข้าถึง",
"requireMFA": "บังคับให้ผู้ใช้ตามบทบาทเปิดใช้งาน MFA"
},
"serverBackups": {
"actions": "คำสั่งด่วน",
@@ -341,7 +345,7 @@
"myBackup": "ข้อมูลสำรองใหม่ของฉัน",
"name": "ชื่อ",
"newBackup": "สร้างข้อมูลสำรองใหม่",
"no-backup": "ไม่มีการสำรองข้อมูล หากต้องการตั้งค่าการสำรองข้อมูลใหม่ กรุณากด สร้างข้อมูลสำรองใหม่",
"no-backup": "ไม่มีข้อมูลสำรอง หากต้องการสร้างการกำหนดค่าสำรองใหม่ โปรดกด \"สร้างข้อมูลสำรองใหม่\"",
"options": "ตัวเลือก",
"path": "เส้นทาง",
"restore": "คืนค่า",
@@ -357,7 +361,8 @@
"storageLocationDesc": "คุณต้องการสำรองข้อมูลไว้ที่ไหน?",
"inplace": "กู้คืนไฟล์สำรองในตำแหน่งเดิม - ไฟล์ที่ไม่มีในไฟล์สำรอง zip จะยังคงอยู่ในไดเรกทอรีของเซิร์ฟเวอร์",
"replace": " .กู้คืนไฟล์สำรองและรีเซ็ตไดเรกทอรีของเซิร์ฟเวอร์ให้มีโครงสร้างเหมือนกับไฟล์สำรองทั้งหมด",
"radioRequired": "คุณต้องเลือกตัวเลือกเพื่อกู้คืนไฟล์สำรองของคุณ"
"radioRequired": "คุณต้องเลือกตัวเลือกเพื่อกู้คืนไฟล์สำรองของคุณ",
"snapshotDisclaimer": "ข้อมูลสำรองแบบ Snapshot ยังอยู่ในช่วงทดสอบ (Beta) ข้อมูลสำรองแบบ Snapshot ควรจะจัดเก็บไฟล์เพียงชุดเดียวและใช้พื้นที่ดิสก์น้อยลงมาก การสำรองและกู้คืนทำงานได้ดีในการทดสอบ แต่คุณอาจพบปัญหาที่คาดไม่ถึง คุณไม่ควรพึ่งพาวิธีการสำรองข้อมูลนี้เพียงอย่างเดียวสำหรับการสำรองข้อมูลที่มีความสำคัญสูงสุด โปรดรายงานปัญหาใด ๆ ที่คุณพบ"
},
"serverConfig": {
"bePatientDelete": "กรุณารออย่างใจเย็นในขณะที่เราลบเซิร์ฟเวอร์ของคุณออกจากแผงควบคุม Crafty หน้าต่างนี้จะปิดลงในอีกสักครู่",
@@ -382,20 +387,20 @@
"noDelete": "ไม่, ย้อนกลับ",
"noDeleteFiles": "ไม่, แค่ลบออกจากแผงควบคุม",
"removeOldLogsAfter": "ลบบัททึกเก่าหลังจาก",
"removeOldLogsAfterDesc": "ไฟล์บันทึกจะต้องเก่ากี่วันจึงจะถูกลบ (ปิด 0)",
"removeOldLogsAfterDesc": "ควรเก็บไฟล์บันทึกข้อมูลไว้เป็นเวลากี่วัน? (0 คือ ปิดการใช้งาน)",
"save": "บันทึก",
"sendingDelete": "กำลังลบเซิร์ฟเวอร์",
"sendingRequest": "กำลังส่งคำขอของคุณ...",
"serverAutoStart": "เริ่มการทำงานเซิร์ฟเวอร์โดยอัตโนมัติ",
"serverAutostartDelay": "หน่วงเวลาการเริ่มต้นโดยอัตโนมัติ",
"serverAutostartDelayDesc": "หน่วงเวลาก่อนเริ่มต้นอัตโนมัติ (หากเปิดใช้งานด้านล่าง)",
"serverAutostartDelayDesc": "หน่วงเวลาก่อนเริ่มทำงานอัตโนมัติ (หากเปิดใช้งานด้านล่างนี้)",
"serverCrashDetection": "การตรวจจับความผิดพลาดของเซิร์ฟเวอร์",
"serverExecutable": "แฟ้มกระทำการของเซิร์ฟเวอร์",
"serverExecutableDesc": "แฟ้มกระทำการที่ใช้ในการทำงานของเซิร์ฟเวอร์ (.jar)",
"serverExecutionCommand": "คำสั่งการดำเนินการเซิร์ฟเวอร์",
"serverExecutionCommandDesc": "สิ่งนี้จะทำงานเบื้องหลังเทอร์มินัล",
"serverIP": "IP เซิร์ฟเวอร์",
"serverIPDesc": "IP ที่ Crafty ใช้เชื่อมต่อเพื่อดูสถิติ (ลองใช้ IP จริงแทน 127.0.0.1 หากคุณพบปัญหา)",
"serverIPDesc": "IP ที่ Crafty ควรเชื่อมต่อเพื่อรับข้อมูลสถิติ (ลองใช้ IP จริงแทน 127.0.0.1 หากคุณพบปัญหา)",
"serverLogLocation": "ตำแหน่งไฟล์บันทึกข้อมูลเซิร์ฟเวอร์",
"serverLogLocationDesc": "เส้นทางไปยังไฟล์บันทึก",
"serverName": "ชื่อเซิร์ฟเวอร์",
@@ -423,7 +428,7 @@
"ขอแนะนำให้ <code>ไม่</code> เปลี่ยนเส้นทางของเซิร์ฟเวอร์ที่จัดการโดย Crafty",
"การเปลี่ยนเส้นทาง <code>สามารถ</code> ทำให้สิ่งต่างๆ เสียหาย โดยเฉพาะบนระบบปฏิบัติการประเภท Linux ที่การอนุญาตของไฟล์ถูกจำกัดไว้มากกว่า",
"<br /><br/>",
"หากคุณรู้สึกว่าต้องเปลี่ยนตำแหน่งของเซิร์ฟเวอร์ คุณสามารถทำได้ตราบใดที่คุณให้สิทธิ์ผู้ใช้ \"crafty\" ในการอ่าน / เขียนไปยังเส้นทางเซิร์ฟเวอร์",
"หากคุณต้องการเปลี่ยนตำแหน่งที่ตั้งของเซิร์ฟเวอร์ คุณสามารถทำได้ ตราบใดที่คุณให้สิทธิ์ผู้ใช้ \"crafty\" ในการอ่าน/เขียนไปยังเส้นทางของเซิร์ฟเวอร์",
"<br />",
"<br />",
"บน Linux วิธีที่ดีที่สุดคือดำเนินการคำสั่งดังต่อไปนี้:<br />",
@@ -567,7 +572,7 @@
},
"serverTerm": {
"commandInput": "ป้อนคำสั่งของคุณ",
"delay-explained": "บริการ/ตัวแทนเพิ่งเริ่มต้นและกำลังชะลอการเริ่มต้นเซิร์ฟเวอร์ Minecraft",
"delay-explained": "บริการ/เอเจนต์เพิ่งเริ่มต้น และกำลังหน่วงเวลาการเริ่มอินสแตนซ์เซิร์ฟเวอร์ Minecraft",
"importing": "กำลังนำเข้า...",
"installing": "กำลังติดตั้ง...",
"restart": "รีสตาร์ท",
@@ -630,7 +635,7 @@
"credits": "เครดิต",
"dashboard": "แผงควบคุม",
"documentation": "เอกสารประกอบ",
"inApp": "เอกสารประกอบภายในแอป",
"inApp": "เอกสารภายในแอป",
"newServer": "สร้างเซิร์ฟเวอร์ใหม่",
"servers": "เซิร์ฟเวอร์",
"panelSettings": "การตั้งค่าของแผงควบคุม"
@@ -653,7 +658,7 @@
"configAreaDesc": "ที่นี่คือที่ที่คุณเปลี่ยนการตั้งค่าผู้ใช้ทั้งหมดของคุณ",
"confirmDelete": "คุณแน่ใจหรือไม่ว่าต้องการลบผู้ใช้รายนี้ การกระทำนี้ไม่สามารถย้อนกลับได้",
"craftyPermDesc": "สิทธิ์เข้าถึง Crafty ที่ผู้ใช้คนนี้มี ",
"craftyPerms": "สิทธิ์เข้าถึง Crafty: ",
"craftyPerms": "สิทธิ์การเข้าถึงของ Crafty: ",
"created": "สร้างเมื่อ: ",
"delSuper": "คุณไม่สามารถลบผู้ใช้ขั้นสูงได้",
"deleteUser": "ลบผู้ใช้: ",
@@ -685,11 +690,12 @@
"userTheme": "ธีม UI",
"uses": "จำนวนการใช้งานที่อนุญาต (-1==ไม่มีขีดจำกัด)",
"changePass": "เปลี่ยนรหัสผ่าน",
"totpHeader": "การยืนยันตัวตนหลายปัจจัย (MFA)",
"totpHeader": "การยืนยันตัวตนแบบหลายปัจจัย (MFA)",
"hints": "เปิดใช้งานคำแนะนำไหม?",
"changeUser": "เปลี่ยนชื่อผู้ใช้",
"editTOTP": "แก้ไข MFA ของผู้ใช้",
"totpIdReq": "จำเป็นต้องมี TOTP ID เพื่อขอการร้องขอ"
"totpIdReq": "จำเป็นต้องใช้ ID MFA สำหรับการร้องขอนี้",
"selfDisable": "คุณไม่สามารถปิดใช้งานบัญชีของคุณเองได้"
},
"validators": {
"roleManager": "ผู้จัดการบทบาทต้องเป็นประเภทจำนวนเต็ม (ID ผู้จัดการ) หรือเป็น None",
@@ -711,8 +717,11 @@
"typeInteger": "ต้องเป็นตัวเลข",
"typeList": "ต้องเป็นประเภทลิสต์/อาร์เรย์ ",
"typeString": "ต้องเป็นประเภทสตริง",
"2FAerror": "รหัสการยืนยันตัวตนหลายปัจจัยต้องประกอบด้วย 6 หลัก (เช่น 000000) หรือรหัสสำรอง 16 ตัวอักษรที่แยกทุก 4 ตัวด้วยเครื่องหมาย - (เช่น ABCD-EFGH-IJKL-MNOP)",
"totp": "รหัส TOTP ต้องประกอบด้วย 6 ตัวเลข"
"2FAerror": "รหัสการยืนยันตัวตนหลายปัจจัย ต้องเป็นตัวเลข 6 หลัก(เช่น 000000) ) หรือเป็นรหัสสำรอง 16 ตัวอักษรที่คั่นด้วยเครื่องหมาย - ทุก 4 ตัว (เช่น ABCD-EFGH-IJKL-MNOP)",
"totp": "รหัส MFA ต้องเป็นตัวเลข 6 หลัก",
"additionalProperties": "ไม่อนุญาตให้ส่งผ่านคุณสมบัติเพิ่มเติม",
"mfaName": "ข้อมูลที่ป้อนต้องเป็นประเภทสตริงและมีอย่างน้อย 3 ตัวอักษรสำหรับคุณสมบัติ",
"passProp": "รหัสผ่านต้องเป็นสตริงที่มีความยาวอย่างน้อย 8 ตัวอักษร"
},
"webhooks": {
"areYouSureDel": "คุณแน่ใจหรือไม่ว่าต้องการลบ Webhook นี้?",
@@ -744,10 +753,10 @@
"verify": "การยืนยัน MFA ล้มเหลว รหัสที่ป้อนไม่ถูกต้อง",
"name": "ชื่อ",
"deleteConfirm": "คุณแน่ใจหรือไม่ว่าต้องการลบวิธี MFA นี้?",
"mfaWarn": "คุณต้องเปิดใช้งาน MFA เพื่อทำการโต้ตอบกับระบบนี้ กรุณาสร้างคีย์ MFA หากคุณเห็นข้อความนี้และได้สร้างคีย์แล้ว กรุณาออกจากระบบแล้วเข้าสู่ระบบใหม่โดยใช้ MFA",
"mfaWarn": "คุณต้องเปิดใช้งาน MFA เพื่อโต้ตอบกับระบบนี้ กรุณาสร้างคีย์ MFA หากคุณเห็นข้อความนี้และได้สร้างคีย์เรียบร้อยแล้ว กรุณาออกจากระบบ จากนั้นเข้าสู่ระบบอีกครั้งด้วย MFA",
"backupCodesMax": "จำนวนรหัสสำรองสูงสุด 6 รหัสถูกใช้ไปแล้ว",
"confirm2FA": "กรุณายืนยันรหัสยืนยันตัวตน",
"newOTP": "เพิ่มการยืนยันตัวตนหลายปัจจัยใหม่",
"newOTP": "เพิ่มวิธีการยืนยันตัวตนแบบหลายปัจจัยใหม่",
"id": "ไอดี",
"config": "กำหนดค่า",
"delete": "ลบ",
@@ -755,18 +764,19 @@
"no": "ไม่",
"2faCreate": "เพิ่มวิธี MFA ใหม่",
"backupCodes": "กรุณาคัดลอกรหัสสำรองของคุณ",
"backupOtp": "จำเป็นต้องมีวิธี MFA เพื่อขอรับรหัสสำรอง",
"backupOtp": "ต้องมีวิธีการ MFA ก่อนจึงจะสามารถร้องขอรหัสสำรองได้",
"saveWarn": "อย่าลืมบันทึกรหัสสำรองเหล่านี้ มิฉะนั้นคุณอาจสูญเสียการเข้าถึง",
"newName": "กรอกชื่อที่จดจำง่ายสำหรับวิธี MFA นี้",
"otpTitle": "วิธีการ MFA",
"renewRecovery": "ต่ออายุรหัสกู้คืน",
"verifyTitle": "ยืนยันวิธี MFA โดยใช้แอปพลิเคชันตัวตรวจสอบ",
"goToPage": "ไปที่หน้า MFA"
"goToPage": "ไปที่หน้า MFA",
"otpReq": "ผู้ใช้นี้จำเป็นต้องมี MFA คุณไม่สามารถลบวิธีการ MFA ที่มีเพียงวิธีเดียวของคุณได้"
},
"configJson": {
"cookie_expire": "คุกกี้เซสชันจะหมดอายุหลังจาก (วัน)",
"allow_nsfw_profile_pictures": "อนุญาตให้ภาพโปรไฟล์ Gravatar™ ที่ไม่เหมาะสมสำหรับทุกวัย (NSFW)",
"delete_default_json": "ลบไฟล์ json ค่าเริ่มต้นเมื่อเริ่มต้นระบบ",
"delete_default_json": "ลบไฟล์ JSON เริ่มต้นเมื่อเริ่มการทำงาน",
"dir_size_poll_freq_minutes": "Crafty ควรตรวจสอบขนาดของไดเรกทอรีเซิร์ฟเวอร์ของคุณบ่อยแค่ไหน (นาที)",
"keywords": "คำสำคัญที่ Crafty ควรเน้นในเทอร์มินัลของเซิร์ฟเวอร์ของคุณ",
"max_login_attempts": "จำนวนครั้งสูงสุดที่ IP ระยะไกลสามารถเข้าสู่ระบบผิดพลาดก่อนที่เราจะทำการหมดเวลาการเข้าสู่ระบบ",
@@ -782,12 +792,18 @@
"False": "เท็จ",
"history_max_age": "Crafty ควรเก็บสถิติของเซิร์ฟเวอร์ไว้เป็นระยะเวลา (วัน)",
"https_port": "พอร์ตของ Crafty",
"language": "ภาษาระบบทั่วโลก (สำหรับหน้าสาธารณะและการแปลสำรอง)",
"language": "ภาษาของระบบหลัก (สำหรับหน้าสาธารณะและการใช้งานเมื่อไม่มีคำแปล)",
"max_log_lines": "จำนวนบรรทัดสูงสุดของบันทึกต่อไฟล์",
"reset_secrets_on_next_boot": "รีเซ็ตรหัสลับในการทำงานครั้งถัดไป",
"show_errors": "แสดงข้อผิดพลาดการดีบักของเว็บเซิร์ฟเวอร์",
"submit": "ส่ง",
"True": "จริง",
"virtual_terminal_lines": "จำนวนบรรทัดที่เราควรอนุญาตในบัฟเฟอร์เทอร์มินัลของเซิร์ฟเวอร์ของคุณ"
"virtual_terminal_lines": "จำนวนบรรทัดที่เราควรอนุญาตในบัฟเฟอร์เทอร์มินัลของเซิร์ฟเวอร์ของคุณ",
"general": "ทั่วไป",
"logs": "การบันทึกข้อมูล",
"miscellaneous": "อื่น ๆ",
"monitoring": "การเฝ้าระวัง",
"security": "ความปลอดภัย",
"superMFA": "กำหนดให้ผู้ใช้ระดับสูง (superusers) ต้องเปิดใช้การยืนยันตัวตนหลายขั้นตอน (MFA)"
}
}