fix(panel): make the LuckPerms page honest about what the server said (#59)
Live with real LuckPerms 5.5.85: every lp command returns an empty RCON body (list/plugins answer normally; a standalone RCON client sees the same, and creategroup/permission-set still persist), so the read projection can never populate and the page asserted "no parent groups / no explicit nodes" for a state it could not actually read. The raw reply now rides the same disclosure the rosters carry, a silent entry-less reply shows an explicit notice instead of the false empty claims, and the write history's placeholder no longer dresses up a fabricated "[RCON] ..." line as output.
This commit is contained in:
4 files changed
+46
-5
No files matched your search
@@ -159,6 +159,8 @@
|
|||||||
"luckperms_revert_success": "Action reverted successfully!",
|
"luckperms_revert_success": "Action reverted successfully!",
|
||||||
"luckperms_presets": "Presets",
|
"luckperms_presets": "Presets",
|
||||||
"luckperms_no_parent_groups": "No parent groups assigned",
|
"luckperms_no_parent_groups": "No parent groups assigned",
|
||||||
|
"luckperms_no_reply": "The server returned no parseable data — some LuckPerms versions do not reply over RCON, so the group/permission lists below may be incomplete. Grants and revocations still take effect.",
|
||||||
|
"luckperms_no_output": "(the server returned no output)",
|
||||||
"luckperms_global": "global",
|
"luckperms_global": "global",
|
||||||
"luckperms_no_perms": "No explicit permission nodes assigned",
|
"luckperms_no_perms": "No explicit permission nodes assigned",
|
||||||
"luckperms_rcon_output": "RCON Console Output",
|
"luckperms_rcon_output": "RCON Console Output",
|
||||||
|
|||||||
@@ -159,6 +159,8 @@
|
|||||||
"luckperms_revert_success": "已成功撤销该操作!",
|
"luckperms_revert_success": "已成功撤销该操作!",
|
||||||
"luckperms_presets": "预设",
|
"luckperms_presets": "预设",
|
||||||
"luckperms_no_parent_groups": "未分配任何父组",
|
"luckperms_no_parent_groups": "未分配任何父组",
|
||||||
|
"luckperms_no_reply": "服务器没有返回可解析的数据——部分 LuckPerms 版本不通过 RCON 回包,下面的用户组/权限列表可能不准确;授予与撤销操作仍然会实际生效。",
|
||||||
|
"luckperms_no_output": "(服务器未返回输出)",
|
||||||
"luckperms_global": "全局",
|
"luckperms_global": "全局",
|
||||||
"luckperms_no_perms": "尚未分配任何权限节点",
|
"luckperms_no_perms": "尚未分配任何权限节点",
|
||||||
"luckperms_rcon_output": "RCON 控制台输出",
|
"luckperms_rcon_output": "RCON 控制台输出",
|
||||||
|
|||||||
@@ -245,6 +245,7 @@ export const api = {
|
|||||||
player: string;
|
player: string;
|
||||||
groups: string[];
|
groups: string[];
|
||||||
permissions: { node: string; value: boolean; world?: string }[];
|
permissions: { node: string; value: boolean; world?: string }[];
|
||||||
|
output: string;
|
||||||
}>("GET", `/servers/${name}/access/luckperms/${player}`),
|
}>("GET", `/servers/${name}/access/luckperms/${player}`),
|
||||||
|
|
||||||
accessPermission: (
|
accessPermission: (
|
||||||
|
|||||||
@@ -88,6 +88,18 @@ export function ServerLuckPerms() {
|
|||||||
[name, selectedPlayer]
|
[name, selectedPlayer]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// LuckPerms does not reply over RCON (its commands finish asynchronously, after
|
||||||
|
// the response is flushed) — verified live on 5.5.85: every `lp` command
|
||||||
|
// returns an empty body while `list`/`plugins` answer normally. A silent,
|
||||||
|
// entry-less reply therefore means "could not read", NOT "no groups / no
|
||||||
|
// permissions": the two projection panels must not claim the latter on their
|
||||||
|
// own. The writes still work, so the page keeps offering them.
|
||||||
|
const lpSilent =
|
||||||
|
!!lpInfo &&
|
||||||
|
!(lpInfo.output ?? "").trim() &&
|
||||||
|
(lpInfo.groups?.length ?? 0) === 0 &&
|
||||||
|
(lpInfo.permissions?.length ?? 0) === 0;
|
||||||
|
|
||||||
// Form State
|
// Form State
|
||||||
const [groupNameInput, setGroupNameInput] = useState("");
|
const [groupNameInput, setGroupNameInput] = useState("");
|
||||||
const [permNodeInput, setPermNodeInput] = useState("");
|
const [permNodeInput, setPermNodeInput] = useState("");
|
||||||
@@ -135,7 +147,7 @@ export function ServerLuckPerms() {
|
|||||||
action: "add",
|
action: "add",
|
||||||
target: groupName,
|
target: groupName,
|
||||||
status: "success",
|
status: "success",
|
||||||
output: res.output || `[RCON] parent add ${groupName}`,
|
output: res.output || t("luckperms_no_output"),
|
||||||
});
|
});
|
||||||
reloadLp();
|
reloadLp();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -157,7 +169,7 @@ export function ServerLuckPerms() {
|
|||||||
action: "remove",
|
action: "remove",
|
||||||
target: groupName,
|
target: groupName,
|
||||||
status: "success",
|
status: "success",
|
||||||
output: res.output || `[RCON] parent remove ${groupName}`,
|
output: res.output || t("luckperms_no_output"),
|
||||||
});
|
});
|
||||||
reloadLp();
|
reloadLp();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -194,7 +206,7 @@ export function ServerLuckPerms() {
|
|||||||
value: permValueInput,
|
value: permValueInput,
|
||||||
world: world || undefined,
|
world: world || undefined,
|
||||||
status: "success",
|
status: "success",
|
||||||
output: res.output || `[RCON] permission set ${node}`,
|
output: res.output || t("luckperms_no_output"),
|
||||||
});
|
});
|
||||||
setPermNodeInput("");
|
setPermNodeInput("");
|
||||||
setPermWorldInput("");
|
setPermWorldInput("");
|
||||||
@@ -219,7 +231,7 @@ export function ServerLuckPerms() {
|
|||||||
target: node,
|
target: node,
|
||||||
world: world || undefined,
|
world: world || undefined,
|
||||||
status: "success",
|
status: "success",
|
||||||
output: res.output || `[RCON] permission unset ${node}`,
|
output: res.output || t("luckperms_no_output"),
|
||||||
});
|
});
|
||||||
reloadLp();
|
reloadLp();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -395,6 +407,30 @@ export function ServerLuckPerms() {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Ground truth: the raw RCON reply behind the two projection panels.
|
||||||
|
The parse is LuckPerms-format-specific — on a server without the
|
||||||
|
plugin it yields zero entries, and without this disclosure the
|
||||||
|
panels would present that as "no groups / no permissions" while
|
||||||
|
the real reply ("Unknown or incomplete command…") stays invisible.
|
||||||
|
Same disclosure the whitelist/ban/online rosters carry. */}
|
||||||
|
{lpInfo?.output?.trim() && (
|
||||||
|
<details className="group/details">
|
||||||
|
<summary className="cursor-pointer select-none text-[10px] text-muted-foreground/70 hover:text-foreground font-mono transition-colors list-none flex items-center gap-1">
|
||||||
|
<span className="transition-transform group-open/details:rotate-90">▶</span>
|
||||||
|
{t("luckperms_rcon_output")}
|
||||||
|
</summary>
|
||||||
|
<pre className="mt-1.5 p-2 rounded bg-muted/60 border border-border/80 font-mono text-[10px] text-foreground/80 overflow-x-auto whitespace-pre-wrap break-all max-h-24">
|
||||||
|
{lpInfo.output}
|
||||||
|
</pre>
|
||||||
|
</details>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{lpSilent && (
|
||||||
|
<div className="rounded-md border border-dashed border-border bg-muted/30 p-3 text-xs text-muted-foreground">
|
||||||
|
{t("luckperms_no_reply")}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Operations Feedback */}
|
{/* Operations Feedback */}
|
||||||
{formFeedback && (
|
{formFeedback && (
|
||||||
<div className={cn(
|
<div className={cn(
|
||||||
@@ -773,4 +809,4 @@ function ownershipPendingCheck(
|
|||||||
mineError: any
|
mineError: any
|
||||||
): boolean {
|
): boolean {
|
||||||
return tierLoading || (!isAdmin && mine === null && !mineError);
|
return tierLoading || (!isAdmin && mine === null && !mineError);
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user