scopedvr-map-core/server/server_store.gd
2026-11-01 19:31:33 +01:00

114 lines
3.5 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

class_name ServerStore
extends RefCounted
## Gespeicherte Admin-Server des Editors (Name, Adresse, Port, https, Token).
##
## Das Token liegt NICHT im Klartext auf der Platte: die Datei ist mit einem
## aus Rechner-ID + Nutzerordner abgeleiteten Schlüssel verschlüsselt. Das hält
## Zufallsleser, Cloud-Sync und Screenshots von Ordnern raus – gegen Schadsoftware
## mit Zugriff auf deinen Benutzer schützt es nicht (dafür gibt es keine
## plattformübergreifende Lösung ohne Passwort-Manager). Tokens werden nie geloggt.
const DEFAULT_PATH := "user://admin_server.dat"
const DEFAULT_PORT := 24580
const MAX_SERVERS := 50
var path: String = DEFAULT_PATH
var servers: Array = [] # [{id, name, host, port, tls, token}]
var last_error: String = ""
func _init(p: String = DEFAULT_PATH) -> void:
path = p
func _key() -> String:
return ("svr-editor-admin|%s|%s" % [OS.get_unique_id(), OS.get_user_data_dir()]).sha256_text()
func load_store() -> void:
servers.clear()
last_error = ""
if not FileAccess.file_exists(path):
return
var fa := FileAccess.open_encrypted_with_pass(path, FileAccess.READ, _key())
if fa == null:
last_error = "Gespeicherte Server konnten nicht gelesen werden (andere Installation oder beschädigte Datei)."
return
var parsed: Variant = JSON.parse_string(fa.get_as_text())
if parsed is Array:
for e in parsed:
if e is Dictionary and str(e.get("host", "")) != "":
servers.append({
"id": str(e.get("id", _new_id())), "name": str(e.get("name", e["host"])),
"host": str(e["host"]), "port": int(e.get("port", DEFAULT_PORT)),
"tls": bool(e.get("tls", false)), "token": str(e.get("token", ""))})
func save_store() -> bool:
var fa := FileAccess.open_encrypted_with_pass(path, FileAccess.WRITE, _key())
if fa == null:
last_error = "Server-Liste konnte nicht gespeichert werden (Fehler %d)." % FileAccess.get_open_error()
return false
fa.store_string(JSON.stringify(servers))
return true
## Neuer oder aktualisierter Eintrag. Rückgabe: id ("" = Eingabe ungültig → last_error).
func upsert(entry: Dictionary) -> String:
var err := AdminClient.check_address(str(entry.get("host", "")), int(entry.get("port", 0)))
if err != "":
last_error = err
return ""
var e := {
"id": str(entry.get("id", "")), "name": str(entry.get("name", "")).strip_edges(),
"host": str(entry["host"]).strip_edges(), "port": int(entry["port"]),
"tls": bool(entry.get("tls", false)), "token": str(entry.get("token", "")).strip_edges()}
if e["name"] == "":
e["name"] = "%s:%d" % [e["host"], e["port"]]
if e["id"] == "":
if servers.size() >= MAX_SERVERS:
last_error = "Maximal %d gespeicherte Server." % MAX_SERVERS
return ""
e["id"] = _new_id()
servers.append(e)
else:
var i := index_of(e["id"])
if i < 0:
servers.append(e)
else:
servers[i] = e
save_store()
return e["id"]
func remove(id: String) -> bool:
var i := index_of(id)
if i < 0:
return false
servers.remove_at(i)
save_store()
return true
func index_of(id: String) -> int:
for i in servers.size():
if str(servers[i]["id"]) == id:
return i
return -1
func get_entry(id: String) -> Dictionary:
var i := index_of(id)
return servers[i].duplicate() if i >= 0 else {}
## Für Anzeige/Logs: nie das Token.
func public_view(id: String) -> Dictionary:
var e := get_entry(id)
e.erase("token")
e["has_token"] = str(get_entry(id).get("token", "")) != ""
return e
static func _new_id() -> String:
return "s%d_%d" % [int(Time.get_unix_time_from_system()), randi() % 100000]