feat(settings): support dual-mode wechat oauth defaults
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
appendAuthSourceDefaultsToUpdateRequest,
|
||||
buildAuthSourceDefaultsState,
|
||||
type UpdateSettingsRequest,
|
||||
} from '@/api/admin/settings'
|
||||
} from "@/api/admin/settings";
|
||||
|
||||
describe('admin settings auth source defaults helpers', () => {
|
||||
it('builds auth source defaults state from flat settings fields', () => {
|
||||
describe("admin settings auth source defaults helpers", () => {
|
||||
it("builds auth source defaults state from flat settings fields", () => {
|
||||
const state = buildAuthSourceDefaultsState({
|
||||
auth_source_default_email_balance: 9.5,
|
||||
auth_source_default_email_concurrency: 3,
|
||||
@@ -23,7 +23,7 @@ describe('admin settings auth source defaults helpers', () => {
|
||||
],
|
||||
auth_source_default_linuxdo_grant_on_signup: true,
|
||||
auth_source_default_linuxdo_grant_on_first_bind: false,
|
||||
})
|
||||
});
|
||||
|
||||
expect(state.email).toEqual({
|
||||
balance: 9.5,
|
||||
@@ -31,34 +31,43 @@ describe('admin settings auth source defaults helpers', () => {
|
||||
subscriptions: [{ group_id: 1, validity_days: 30 }],
|
||||
grant_on_signup: false,
|
||||
grant_on_first_bind: true,
|
||||
})
|
||||
});
|
||||
expect(state.linuxdo).toEqual({
|
||||
balance: 6,
|
||||
concurrency: 8,
|
||||
subscriptions: [{ group_id: 2, validity_days: 60 }],
|
||||
grant_on_signup: true,
|
||||
grant_on_first_bind: false,
|
||||
})
|
||||
});
|
||||
expect(state.oidc).toEqual({
|
||||
balance: 0,
|
||||
concurrency: 5,
|
||||
subscriptions: [],
|
||||
grant_on_signup: true,
|
||||
grant_on_signup: false,
|
||||
grant_on_first_bind: false,
|
||||
})
|
||||
});
|
||||
expect(state.wechat).toEqual({
|
||||
balance: 0,
|
||||
concurrency: 5,
|
||||
subscriptions: [],
|
||||
grant_on_signup: true,
|
||||
grant_on_signup: false,
|
||||
grant_on_first_bind: false,
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
it('appends auth source defaults back onto update payload', () => {
|
||||
it("defaults grant-on-signup to disabled when settings are missing", () => {
|
||||
const state = buildAuthSourceDefaultsState({});
|
||||
|
||||
expect(state.email.grant_on_signup).toBe(false);
|
||||
expect(state.linuxdo.grant_on_signup).toBe(false);
|
||||
expect(state.oidc.grant_on_signup).toBe(false);
|
||||
expect(state.wechat.grant_on_signup).toBe(false);
|
||||
});
|
||||
|
||||
it("appends auth source defaults back onto update payload", () => {
|
||||
const payload: UpdateSettingsRequest = {
|
||||
site_name: 'Sub2API',
|
||||
}
|
||||
site_name: "Sub2API",
|
||||
};
|
||||
|
||||
appendAuthSourceDefaultsToUpdateRequest(payload, {
|
||||
email: {
|
||||
@@ -89,13 +98,15 @@ describe('admin settings auth source defaults helpers', () => {
|
||||
grant_on_signup: false,
|
||||
grant_on_first_bind: false,
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
expect(payload).toMatchObject({
|
||||
site_name: 'Sub2API',
|
||||
site_name: "Sub2API",
|
||||
auth_source_default_email_balance: 1.25,
|
||||
auth_source_default_email_concurrency: 2,
|
||||
auth_source_default_email_subscriptions: [{ group_id: 3, validity_days: 7 }],
|
||||
auth_source_default_email_subscriptions: [
|
||||
{ group_id: 3, validity_days: 7 },
|
||||
],
|
||||
auth_source_default_email_grant_on_signup: true,
|
||||
auth_source_default_email_grant_on_first_bind: false,
|
||||
auth_source_default_linuxdo_balance: 0,
|
||||
@@ -105,7 +116,9 @@ describe('admin settings auth source defaults helpers', () => {
|
||||
auth_source_default_linuxdo_grant_on_first_bind: true,
|
||||
auth_source_default_oidc_balance: 4,
|
||||
auth_source_default_oidc_concurrency: 9,
|
||||
auth_source_default_oidc_subscriptions: [{ group_id: 9, validity_days: 90 }],
|
||||
auth_source_default_oidc_subscriptions: [
|
||||
{ group_id: 9, validity_days: 90 },
|
||||
],
|
||||
auth_source_default_oidc_grant_on_signup: true,
|
||||
auth_source_default_oidc_grant_on_first_bind: true,
|
||||
auth_source_default_wechat_balance: 2,
|
||||
@@ -113,6 +126,6 @@ describe('admin settings auth source defaults helpers', () => {
|
||||
auth_source_default_wechat_subscriptions: [],
|
||||
auth_source_default_wechat_grant_on_signup: false,
|
||||
auth_source_default_wechat_grant_on_first_bind: false,
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -167,7 +167,7 @@ export function buildAuthSourceDefaultsState(
|
||||
: [],
|
||||
),
|
||||
grant_on_signup:
|
||||
raw[`auth_source_default_${source}_grant_on_signup`] !== false,
|
||||
raw[`auth_source_default_${source}_grant_on_signup`] === true,
|
||||
grant_on_first_bind:
|
||||
raw[`auth_source_default_${source}_grant_on_first_bind`] === true,
|
||||
};
|
||||
@@ -239,6 +239,33 @@ export function defaultWeChatConnectScopesForMode(mode: unknown): string {
|
||||
: "snsapi_login";
|
||||
}
|
||||
|
||||
export function resolveWeChatConnectModeCapabilities(
|
||||
openEnabled: unknown,
|
||||
mpEnabled: unknown,
|
||||
legacyMode: unknown,
|
||||
): { openEnabled: boolean; mpEnabled: boolean } {
|
||||
if (typeof openEnabled === "boolean" || typeof mpEnabled === "boolean") {
|
||||
return {
|
||||
openEnabled: openEnabled === true,
|
||||
mpEnabled: mpEnabled === true,
|
||||
};
|
||||
}
|
||||
|
||||
return normalizeWeChatConnectMode(legacyMode) === "mp"
|
||||
? { openEnabled: false, mpEnabled: true }
|
||||
: { openEnabled: true, mpEnabled: false };
|
||||
}
|
||||
|
||||
export function deriveWeChatConnectStoredMode(
|
||||
openEnabled: boolean,
|
||||
mpEnabled: boolean,
|
||||
legacyMode: unknown,
|
||||
): WeChatConnectMode {
|
||||
if (mpEnabled) return "mp";
|
||||
if (openEnabled) return "open";
|
||||
return normalizeWeChatConnectMode(legacyMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* System settings interface
|
||||
*/
|
||||
@@ -315,6 +342,8 @@ export interface SystemSettings {
|
||||
wechat_connect_enabled: boolean;
|
||||
wechat_connect_app_id: string;
|
||||
wechat_connect_app_secret_configured: boolean;
|
||||
wechat_connect_open_enabled?: boolean;
|
||||
wechat_connect_mp_enabled?: boolean;
|
||||
wechat_connect_mode: string;
|
||||
wechat_connect_scopes: string;
|
||||
wechat_connect_redirect_url: string;
|
||||
@@ -472,6 +501,8 @@ export interface UpdateSettingsRequest {
|
||||
wechat_connect_enabled?: boolean;
|
||||
wechat_connect_app_id?: string;
|
||||
wechat_connect_app_secret?: string;
|
||||
wechat_connect_open_enabled?: boolean;
|
||||
wechat_connect_mp_enabled?: boolean;
|
||||
wechat_connect_mode?: string;
|
||||
wechat_connect_scopes?: string;
|
||||
wechat_connect_redirect_url?: string;
|
||||
|
||||
@@ -1408,7 +1408,7 @@
|
||||
v-if="form.wechat_connect_enabled"
|
||||
class="space-y-6 border-t border-gray-100 pt-4 dark:border-dark-700"
|
||||
>
|
||||
<div class="grid grid-cols-1 gap-6 lg:grid-cols-3">
|
||||
<div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
||||
<div>
|
||||
<label
|
||||
class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
|
||||
@@ -1463,68 +1463,73 @@
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
||||
<div class="space-y-3">
|
||||
<label
|
||||
class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
{{ localText("模式", "Mode") }}
|
||||
</label>
|
||||
<select
|
||||
data-testid="wechat-connect-mode"
|
||||
v-model="form.wechat_connect_mode"
|
||||
class="input font-mono text-sm"
|
||||
@change="syncWeChatConnectMode"
|
||||
<div
|
||||
class="flex items-center justify-between rounded border border-gray-200 px-4 py-3 dark:border-dark-700"
|
||||
>
|
||||
<option value="open">
|
||||
{{ localText("开放平台", "Open Platform") }}
|
||||
</option>
|
||||
<option value="mp">
|
||||
{{
|
||||
localText(
|
||||
"公众号 / 小程序",
|
||||
"Official Account / Mini Program",
|
||||
)
|
||||
}}
|
||||
</option>
|
||||
</select>
|
||||
<p class="mt-1.5 text-xs text-gray-500 dark:text-gray-400">
|
||||
{{
|
||||
localText(
|
||||
"open 对应微信开放平台,mp 对应公众号/小程序授权。",
|
||||
"open maps to WeChat Open Platform, mp maps to Official Account / Mini Program authorization.",
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
||||
<div>
|
||||
<label
|
||||
class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
|
||||
<div>
|
||||
<div class="font-medium text-gray-900 dark:text-white">
|
||||
{{
|
||||
localText(
|
||||
"非微信环境使用开放平台",
|
||||
"Use Open outside WeChat",
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
<p
|
||||
class="mt-0.5 text-xs text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
{{
|
||||
localText(
|
||||
"浏览器不在微信内时,自动走开放平台扫码授权。",
|
||||
"Use Open Platform QR authorization outside the WeChat browser.",
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
<Toggle
|
||||
v-model="form.wechat_connect_open_enabled"
|
||||
data-testid="wechat-connect-open-enabled"
|
||||
@update:model-value="syncWeChatConnectMode"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="flex items-center justify-between rounded border border-gray-200 px-4 py-3 dark:border-dark-700"
|
||||
>
|
||||
{{ localText("Scopes", "Scopes") }}
|
||||
</label>
|
||||
<input
|
||||
data-testid="wechat-connect-scopes"
|
||||
v-model="form.wechat_connect_scopes"
|
||||
type="text"
|
||||
class="input font-mono text-sm"
|
||||
:placeholder="
|
||||
form.wechat_connect_mode === 'mp'
|
||||
? 'snsapi_userinfo'
|
||||
: 'snsapi_login'
|
||||
"
|
||||
/>
|
||||
<p class="mt-1.5 text-xs text-gray-500 dark:text-gray-400">
|
||||
{{
|
||||
localText(
|
||||
"留空时会按模式自动回填默认值。",
|
||||
"Leave empty to use the default scope for the selected mode.",
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<div>
|
||||
<div class="font-medium text-gray-900 dark:text-white">
|
||||
{{
|
||||
localText(
|
||||
"微信环境使用公众号",
|
||||
"Use MP inside WeChat",
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
<p
|
||||
class="mt-0.5 text-xs text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
{{
|
||||
localText(
|
||||
"浏览器在微信内时,自动走公众号授权。",
|
||||
"Use Official Account authorization inside the WeChat browser.",
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
<Toggle
|
||||
v-model="form.wechat_connect_mp_enabled"
|
||||
data-testid="wechat-connect-mp-enabled"
|
||||
@update:model-value="syncWeChatConnectMode"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -2246,83 +2251,77 @@
|
||||
<Toggle v-model="form.force_email_on_third_party_signup" />
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 xl:grid-cols-2">
|
||||
<div class="space-y-4">
|
||||
<div
|
||||
v-for="authSource in authSourceDefaultsMeta"
|
||||
:key="authSource.source"
|
||||
class="rounded-xl border border-gray-200 p-4 dark:border-dark-700"
|
||||
>
|
||||
<div class="mb-4">
|
||||
<div class="font-medium text-gray-900 dark:text-white">
|
||||
{{ authSource.title }}
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div>
|
||||
<div class="font-medium text-gray-900 dark:text-white">
|
||||
{{ authSource.title }}
|
||||
</div>
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ authSource.description }}
|
||||
</p>
|
||||
</div>
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ authSource.description }}
|
||||
<Toggle
|
||||
v-model="
|
||||
authSourceDefaults[authSource.source].grant_on_signup
|
||||
"
|
||||
:data-testid="`auth-source-${authSource.source}-enabled`"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="authSourceDefaults[authSource.source].grant_on_signup"
|
||||
:data-testid="`auth-source-${authSource.source}-panel`"
|
||||
class="mt-4 space-y-4 border-t border-gray-100 pt-4 dark:border-dark-700"
|
||||
>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||
{{
|
||||
localText(
|
||||
"以下默认值会在该来源注册新用户时发放;首次绑定时授权仅作用于已有账号绑定该来源。",
|
||||
"These defaults apply when a new user registers through this source. Grant on first bind only applies when an existing user binds this source.",
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div>
|
||||
<label
|
||||
class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
{{ t("admin.settings.defaults.defaultBalance") }}
|
||||
</label>
|
||||
<input
|
||||
v-model.number="
|
||||
authSourceDefaults[authSource.source].balance
|
||||
"
|
||||
type="number"
|
||||
step="0.01"
|
||||
min="0"
|
||||
class="input"
|
||||
placeholder="0.00"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
{{ t("admin.settings.defaults.defaultConcurrency") }}
|
||||
</label>
|
||||
<input
|
||||
v-model.number="
|
||||
authSourceDefaults[authSource.source].concurrency
|
||||
"
|
||||
type="number"
|
||||
min="1"
|
||||
class="input"
|
||||
placeholder="5"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 grid grid-cols-1 gap-3 md:grid-cols-2">
|
||||
<div
|
||||
class="flex items-center justify-between rounded border border-gray-200 px-4 py-3 dark:border-dark-700"
|
||||
>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div>
|
||||
<label
|
||||
class="font-medium text-gray-900 dark:text-white"
|
||||
class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
{{ localText("注册即授权", "Grant on signup") }}
|
||||
{{ t("admin.settings.defaults.defaultBalance") }}
|
||||
</label>
|
||||
<p
|
||||
class="mt-0.5 text-xs text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
{{
|
||||
localText(
|
||||
"来源首次注册成功后立即发放默认权益。",
|
||||
"Grant default entitlements immediately after signup.",
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<input
|
||||
v-model.number="
|
||||
authSourceDefaults[authSource.source].balance
|
||||
"
|
||||
type="number"
|
||||
step="0.01"
|
||||
min="0"
|
||||
class="input"
|
||||
placeholder="0.00"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
{{ t("admin.settings.defaults.defaultConcurrency") }}
|
||||
</label>
|
||||
<input
|
||||
v-model.number="
|
||||
authSourceDefaults[authSource.source].concurrency
|
||||
"
|
||||
type="number"
|
||||
min="1"
|
||||
class="input"
|
||||
placeholder="5"
|
||||
/>
|
||||
</div>
|
||||
<Toggle
|
||||
v-model="
|
||||
authSourceDefaults[authSource.source].grant_on_signup
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
@@ -2341,8 +2340,8 @@
|
||||
>
|
||||
{{
|
||||
localText(
|
||||
"来源首次绑定到现有账号时发放默认权益。",
|
||||
"Grant default entitlements when the source is first bound to an existing user.",
|
||||
"已有账号首次绑定该来源时发放默认权益。",
|
||||
"Grant default entitlements when an existing user first binds this source.",
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
@@ -2354,11 +2353,7 @@
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="mt-4 border-t border-gray-100 pt-4 dark:border-dark-700"
|
||||
>
|
||||
<div class="mb-3 flex items-center justify-between">
|
||||
<div>
|
||||
<label
|
||||
@@ -4710,12 +4705,13 @@ import { useI18n } from "vue-i18n";
|
||||
import { adminAPI } from "@/api";
|
||||
import {
|
||||
appendAuthSourceDefaultsToUpdateRequest,
|
||||
defaultWeChatConnectScopesForMode,
|
||||
buildAuthSourceDefaultsState,
|
||||
defaultWeChatConnectScopesForMode,
|
||||
deriveWeChatConnectStoredMode,
|
||||
getPaymentVisibleMethodSourceOptions,
|
||||
normalizePaymentVisibleMethodSource,
|
||||
normalizeDefaultSubscriptionSettings,
|
||||
normalizeWeChatConnectMode,
|
||||
resolveWeChatConnectModeCapabilities,
|
||||
} from "@/api/admin/settings";
|
||||
import type {
|
||||
AuthSourceDefaultsState,
|
||||
@@ -4859,11 +4855,16 @@ interface DefaultSubscriptionGroupOption {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
type SettingsForm = SystemSettings & {
|
||||
type SettingsForm = Omit<
|
||||
SystemSettings,
|
||||
"wechat_connect_open_enabled" | "wechat_connect_mp_enabled"
|
||||
> & {
|
||||
smtp_password: string;
|
||||
turnstile_secret_key: string;
|
||||
linuxdo_connect_client_secret: string;
|
||||
wechat_connect_app_secret: string;
|
||||
wechat_connect_open_enabled: boolean;
|
||||
wechat_connect_mp_enabled: boolean;
|
||||
oidc_connect_client_secret: string;
|
||||
force_email_on_third_party_signup: boolean;
|
||||
payment_visible_method_alipay_source: string;
|
||||
@@ -4958,6 +4959,8 @@ const form = reactive<SettingsForm>({
|
||||
wechat_connect_app_id: "",
|
||||
wechat_connect_app_secret: "",
|
||||
wechat_connect_app_secret_configured: false,
|
||||
wechat_connect_open_enabled: false,
|
||||
wechat_connect_mp_enabled: false,
|
||||
wechat_connect_mode: "open",
|
||||
wechat_connect_scopes: "snsapi_login",
|
||||
wechat_connect_redirect_url: "",
|
||||
@@ -5452,14 +5455,21 @@ const wechatRedirectUrlSuggestion = computed(() => {
|
||||
});
|
||||
|
||||
function syncWeChatConnectMode() {
|
||||
form.wechat_connect_mode = normalizeWeChatConnectMode(
|
||||
const capabilities = resolveWeChatConnectModeCapabilities(
|
||||
form.wechat_connect_open_enabled,
|
||||
form.wechat_connect_mp_enabled,
|
||||
form.wechat_connect_mode,
|
||||
);
|
||||
form.wechat_connect_open_enabled = capabilities.openEnabled;
|
||||
form.wechat_connect_mp_enabled = capabilities.mpEnabled;
|
||||
form.wechat_connect_mode = deriveWeChatConnectStoredMode(
|
||||
capabilities.openEnabled,
|
||||
capabilities.mpEnabled,
|
||||
form.wechat_connect_mode,
|
||||
);
|
||||
form.wechat_connect_scopes = defaultWeChatConnectScopesForMode(
|
||||
form.wechat_connect_mode,
|
||||
);
|
||||
if (!form.wechat_connect_scopes.trim()) {
|
||||
form.wechat_connect_scopes = defaultWeChatConnectScopesForMode(
|
||||
form.wechat_connect_mode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function setAndCopyWeChatRedirectUrl() {
|
||||
@@ -5608,16 +5618,21 @@ async function loadSettings() {
|
||||
form.turnstile_secret_key = "";
|
||||
form.linuxdo_connect_client_secret = "";
|
||||
form.wechat_connect_app_secret = "";
|
||||
form.wechat_connect_mode = normalizeWeChatConnectMode(
|
||||
const wechatCapabilities = resolveWeChatConnectModeCapabilities(
|
||||
settings.wechat_connect_open_enabled,
|
||||
settings.wechat_connect_mp_enabled,
|
||||
settings.wechat_connect_mode,
|
||||
);
|
||||
const wechatConnectScopes =
|
||||
typeof settings.wechat_connect_scopes === "string"
|
||||
? settings.wechat_connect_scopes.trim()
|
||||
: "";
|
||||
form.wechat_connect_scopes =
|
||||
wechatConnectScopes ||
|
||||
defaultWeChatConnectScopesForMode(form.wechat_connect_mode);
|
||||
form.wechat_connect_open_enabled = wechatCapabilities.openEnabled;
|
||||
form.wechat_connect_mp_enabled = wechatCapabilities.mpEnabled;
|
||||
form.wechat_connect_mode = deriveWeChatConnectStoredMode(
|
||||
wechatCapabilities.openEnabled,
|
||||
wechatCapabilities.mpEnabled,
|
||||
settings.wechat_connect_mode,
|
||||
);
|
||||
form.wechat_connect_scopes = defaultWeChatConnectScopesForMode(
|
||||
form.wechat_connect_mode,
|
||||
);
|
||||
form.oidc_connect_client_secret = "";
|
||||
|
||||
// Load web search emulation config separately
|
||||
@@ -5789,6 +5804,12 @@ async function saveSettings() {
|
||||
// Optional URL fields: auto-clear invalid values so they don't cause backend 400 errors
|
||||
if (!isValidHttpUrl(form.frontend_url)) form.frontend_url = "";
|
||||
if (!isValidHttpUrl(form.doc_url)) form.doc_url = "";
|
||||
syncWeChatConnectMode();
|
||||
const wechatStoredMode = deriveWeChatConnectStoredMode(
|
||||
form.wechat_connect_open_enabled,
|
||||
form.wechat_connect_mp_enabled,
|
||||
form.wechat_connect_mode,
|
||||
);
|
||||
|
||||
const payload: UpdateSettingsRequest = {
|
||||
registration_enabled: form.registration_enabled,
|
||||
@@ -5837,10 +5858,11 @@ async function saveSettings() {
|
||||
wechat_connect_enabled: form.wechat_connect_enabled,
|
||||
wechat_connect_app_id: form.wechat_connect_app_id,
|
||||
wechat_connect_app_secret: form.wechat_connect_app_secret || undefined,
|
||||
wechat_connect_mode: normalizeWeChatConnectMode(form.wechat_connect_mode),
|
||||
wechat_connect_open_enabled: form.wechat_connect_open_enabled,
|
||||
wechat_connect_mp_enabled: form.wechat_connect_mp_enabled,
|
||||
wechat_connect_mode: wechatStoredMode,
|
||||
wechat_connect_scopes:
|
||||
form.wechat_connect_scopes.trim() ||
|
||||
defaultWeChatConnectScopesForMode(form.wechat_connect_mode),
|
||||
defaultWeChatConnectScopesForMode(wechatStoredMode),
|
||||
wechat_connect_redirect_url: form.wechat_connect_redirect_url,
|
||||
wechat_connect_frontend_redirect_url:
|
||||
form.wechat_connect_frontend_redirect_url,
|
||||
@@ -5967,16 +5989,21 @@ async function saveSettings() {
|
||||
form.turnstile_secret_key = "";
|
||||
form.linuxdo_connect_client_secret = "";
|
||||
form.wechat_connect_app_secret = "";
|
||||
form.wechat_connect_mode = normalizeWeChatConnectMode(
|
||||
const updatedWechatCapabilities = resolveWeChatConnectModeCapabilities(
|
||||
updated.wechat_connect_open_enabled,
|
||||
updated.wechat_connect_mp_enabled,
|
||||
updated.wechat_connect_mode,
|
||||
);
|
||||
const updatedWechatConnectScopes =
|
||||
typeof updated.wechat_connect_scopes === "string"
|
||||
? updated.wechat_connect_scopes.trim()
|
||||
: "";
|
||||
form.wechat_connect_scopes =
|
||||
updatedWechatConnectScopes ||
|
||||
defaultWeChatConnectScopesForMode(form.wechat_connect_mode);
|
||||
form.wechat_connect_open_enabled = updatedWechatCapabilities.openEnabled;
|
||||
form.wechat_connect_mp_enabled = updatedWechatCapabilities.mpEnabled;
|
||||
form.wechat_connect_mode = deriveWeChatConnectStoredMode(
|
||||
updatedWechatCapabilities.openEnabled,
|
||||
updatedWechatCapabilities.mpEnabled,
|
||||
updated.wechat_connect_mode,
|
||||
);
|
||||
form.wechat_connect_scopes = defaultWeChatConnectScopesForMode(
|
||||
form.wechat_connect_mode,
|
||||
);
|
||||
form.oidc_connect_client_secret = "";
|
||||
// Save web search emulation config separately (errors handled internally)
|
||||
const wsOk = await saveWebSearchConfig();
|
||||
|
||||
@@ -111,9 +111,11 @@ const ToggleStub = defineComponent({
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
setup(props, { emit }) {
|
||||
inheritAttrs: false,
|
||||
setup(props, { attrs, emit }) {
|
||||
return () =>
|
||||
h("input", {
|
||||
...attrs,
|
||||
class: "toggle-stub",
|
||||
type: "checkbox",
|
||||
checked: props.modelValue,
|
||||
@@ -217,6 +219,8 @@ const baseSettingsResponse = {
|
||||
wechat_connect_enabled: true,
|
||||
wechat_connect_app_id: "wx-app-id-123",
|
||||
wechat_connect_app_secret_configured: true,
|
||||
wechat_connect_open_enabled: false,
|
||||
wechat_connect_mp_enabled: true,
|
||||
wechat_connect_mode: "mp",
|
||||
wechat_connect_scopes: "",
|
||||
wechat_connect_redirect_url:
|
||||
@@ -334,6 +338,16 @@ async function openSecurityTab(wrapper: ReturnType<typeof mountView>) {
|
||||
await flushPromises();
|
||||
}
|
||||
|
||||
async function openUsersTab(wrapper: ReturnType<typeof mountView>) {
|
||||
const usersTabButton = wrapper
|
||||
.findAll("button")
|
||||
.find((node) => node.text().includes("admin.settings.tabs.users"));
|
||||
|
||||
expect(usersTabButton).toBeDefined();
|
||||
await usersTabButton?.trigger("click");
|
||||
await flushPromises();
|
||||
}
|
||||
|
||||
describe("admin SettingsView payment visible method controls", () => {
|
||||
beforeEach(() => {
|
||||
getSettings.mockReset();
|
||||
@@ -595,16 +609,19 @@ describe("admin SettingsView wechat connect controls", () => {
|
||||
).toBe("wx-app-id-123");
|
||||
expect(
|
||||
(
|
||||
wrapper.get('[data-testid="wechat-connect-mode"]')
|
||||
.element as HTMLSelectElement
|
||||
).value,
|
||||
).toBe("mp");
|
||||
wrapper.get('[data-testid="wechat-connect-open-enabled"]')
|
||||
.element as HTMLInputElement
|
||||
).checked,
|
||||
).toBe(false);
|
||||
expect(
|
||||
(
|
||||
wrapper.get('[data-testid="wechat-connect-scopes"]')
|
||||
wrapper.get('[data-testid="wechat-connect-mp-enabled"]')
|
||||
.element as HTMLInputElement
|
||||
).value,
|
||||
).toBe("snsapi_userinfo");
|
||||
).checked,
|
||||
).toBe(true);
|
||||
expect(wrapper.find('[data-testid="wechat-connect-scopes"]').exists()).toBe(
|
||||
false,
|
||||
);
|
||||
expect(
|
||||
wrapper
|
||||
.get('[data-testid="wechat-connect-app-secret"]')
|
||||
@@ -630,10 +647,12 @@ describe("admin SettingsView wechat connect controls", () => {
|
||||
await wrapper
|
||||
.get('[data-testid="wechat-connect-app-secret"]')
|
||||
.setValue("new-secret");
|
||||
await wrapper.get('[data-testid="wechat-connect-mode"]').setValue("open");
|
||||
await wrapper
|
||||
.get('[data-testid="wechat-connect-scopes"]')
|
||||
.setValue(" snsapi_base ");
|
||||
.get('[data-testid="wechat-connect-open-enabled"]')
|
||||
.setValue(true);
|
||||
await wrapper
|
||||
.get('[data-testid="wechat-connect-mp-enabled"]')
|
||||
.setValue(true);
|
||||
await wrapper
|
||||
.get('[data-testid="wechat-connect-redirect-url"]')
|
||||
.setValue("https://admin.example.com/api/v1/auth/oauth/wechat/callback");
|
||||
@@ -649,8 +668,8 @@ describe("admin SettingsView wechat connect controls", () => {
|
||||
wechat_connect_enabled: true,
|
||||
wechat_connect_app_id: "wx-app-id-updated",
|
||||
wechat_connect_app_secret: "new-secret",
|
||||
wechat_connect_mode: "open",
|
||||
wechat_connect_scopes: "snsapi_base",
|
||||
wechat_connect_open_enabled: true,
|
||||
wechat_connect_mp_enabled: true,
|
||||
wechat_connect_redirect_url:
|
||||
"https://admin.example.com/api/v1/auth/oauth/wechat/callback",
|
||||
wechat_connect_frontend_redirect_url: "/auth/wechat/callback",
|
||||
@@ -668,4 +687,31 @@ describe("admin SettingsView wechat connect controls", () => {
|
||||
.attributes("placeholder"),
|
||||
).toContain("密钥已配置");
|
||||
});
|
||||
|
||||
it("collapses auth source defaults until the source is enabled", async () => {
|
||||
const wrapper = mountView();
|
||||
|
||||
await flushPromises();
|
||||
await openUsersTab(wrapper);
|
||||
|
||||
expect(
|
||||
(
|
||||
wrapper.get('[data-testid="auth-source-email-enabled"]')
|
||||
.element as HTMLInputElement
|
||||
).checked,
|
||||
).toBe(false);
|
||||
expect(
|
||||
wrapper.find('[data-testid="auth-source-email-panel"]').exists(),
|
||||
).toBe(false);
|
||||
expect(wrapper.text()).not.toContain("注册即授权");
|
||||
|
||||
await wrapper
|
||||
.get('[data-testid="auth-source-email-enabled"]')
|
||||
.setValue(true);
|
||||
|
||||
expect(
|
||||
wrapper.find('[data-testid="auth-source-email-panel"]').exists(),
|
||||
).toBe(true);
|
||||
expect(wrapper.text()).toContain("首次绑定时授权");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user