fix(warp): set license against Cloudflare API and surface errors inline
The license update was always failing because the Cloudflare response has no `success` field — the check rejected every successful PUT. On real errors (e.g. "Too many connected devices."), the toast leaked the raw URL + JSON body. Now the WARP API's error envelope is parsed into a clean message and shown inline next to the Update button.
This commit is contained in:
@@ -27,6 +27,7 @@ const loading = ref(false);
|
|||||||
const warpData = ref(null);
|
const warpData = ref(null);
|
||||||
const warpConfig = ref(null);
|
const warpConfig = ref(null);
|
||||||
const warpPlus = ref('');
|
const warpPlus = ref('');
|
||||||
|
const licenseError = ref('');
|
||||||
// Held in memory so the parent's add/reset handlers receive the same
|
// Held in memory so the parent's add/reset handlers receive the same
|
||||||
// object the modal computed from getConfig().
|
// object the modal computed from getConfig().
|
||||||
const stagedOutbound = ref(null);
|
const stagedOutbound = ref(null);
|
||||||
@@ -41,6 +42,7 @@ watch(() => props.open, (next) => {
|
|||||||
if (!next) return;
|
if (!next) return;
|
||||||
warpConfig.value = null;
|
warpConfig.value = null;
|
||||||
stagedOutbound.value = null;
|
stagedOutbound.value = null;
|
||||||
|
licenseError.value = '';
|
||||||
fetchData();
|
fetchData();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -89,12 +91,15 @@ async function getConfig() {
|
|||||||
async function updateLicense() {
|
async function updateLicense() {
|
||||||
if (warpPlus.value.length < 26) return;
|
if (warpPlus.value.length < 26) return;
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
licenseError.value = '';
|
||||||
try {
|
try {
|
||||||
const msg = await HttpUtil.post('/panel/xray/warp/license', { license: warpPlus.value });
|
const msg = await HttpUtil.post('/panel/xray/warp/license', { license: warpPlus.value });
|
||||||
if (msg?.success) {
|
if (msg?.success) {
|
||||||
warpData.value = JSON.parse(msg.obj);
|
warpData.value = JSON.parse(msg.obj);
|
||||||
warpConfig.value = null;
|
warpConfig.value = null;
|
||||||
warpPlus.value = '';
|
warpPlus.value = '';
|
||||||
|
} else {
|
||||||
|
licenseError.value = msg?.msg || 'Failed to set WARP license.';
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
@@ -233,9 +238,12 @@ const hasConfig = computed(() => !ObjectUtil.isEmpty(warpConfig.value));
|
|||||||
<a-collapse-panel header="WARP / WARP+ license key">
|
<a-collapse-panel header="WARP / WARP+ license key">
|
||||||
<a-form :colon="false" :label-col="{ md: { span: 6 } }" :wrapper-col="{ md: { span: 14 } }">
|
<a-form :colon="false" :label-col="{ md: { span: 6 } }" :wrapper-col="{ md: { span: 14 } }">
|
||||||
<a-form-item label="Key">
|
<a-form-item label="Key">
|
||||||
<a-input v-model:value="warpPlus" placeholder="26-char WARP+ key" />
|
<a-input v-model:value="warpPlus" placeholder="26-char WARP+ key" @update:value="licenseError = ''" />
|
||||||
<a-button type="primary" class="mt-8" :disabled="warpPlus.length < 26" :loading="loading"
|
<div class="license-actions mt-8">
|
||||||
@click="updateLicense">Update</a-button>
|
<a-button type="primary" :disabled="warpPlus.length < 26" :loading="loading"
|
||||||
|
@click="updateLicense">Update</a-button>
|
||||||
|
<a-alert v-if="licenseError" :message="licenseError" type="error" show-icon class="license-error" />
|
||||||
|
</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
@@ -358,4 +366,16 @@ const hasConfig = computed(() => !ObjectUtil.isEmpty(warpConfig.value));
|
|||||||
.ml-8 {
|
.ml-8 {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.license-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.license-error {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+20
-7
@@ -152,13 +152,8 @@ func (s *WarpService) SetWarpLicense(license string) (string, error) {
|
|||||||
if err := json.Unmarshal(body, &response); err != nil {
|
if err := json.Unmarshal(body, &response); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if success, _ := response["success"].(bool); !success {
|
if _, ok := response["id"].(string); !ok {
|
||||||
if errorArr, ok := response["errors"].([]any); ok && len(errorArr) > 0 {
|
return "", common.NewErrorf("warp set license failed: unexpected response: %s", string(body))
|
||||||
if errorObj, ok := errorArr[0].(map[string]any); ok {
|
|
||||||
return "", common.NewError(errorObj["code"], errorObj["message"])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", common.NewError("warp set license failed: unknown error")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
warpData["license_key"] = license
|
warpData["license_key"] = license
|
||||||
@@ -202,8 +197,26 @@ func doWarpRequest(req *http.Request) ([]byte, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||||
|
if msg := parseWarpError(body); msg != "" {
|
||||||
|
return nil, common.NewError(msg)
|
||||||
|
}
|
||||||
return nil, common.NewErrorf("warp api %s %s returned status %d: %s",
|
return nil, common.NewErrorf("warp api %s %s returned status %d: %s",
|
||||||
req.Method, req.URL.Path, resp.StatusCode, string(body))
|
req.Method, req.URL.Path, resp.StatusCode, string(body))
|
||||||
}
|
}
|
||||||
return body, nil
|
return body, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseWarpError(body []byte) string {
|
||||||
|
var env struct {
|
||||||
|
Errors []struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
} `json:"errors"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(body, &env); err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if len(env.Errors) == 0 || env.Errors[0].Message == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return env.Errors[0].Message
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user