Improve local auth env handling and fallbacks

Allow local env files to reliably override authentication feature flags (SMS/WeChat) by whitelisting keys in scripts/dev-utils.mjs and adding a unit test. Add SMS checks to scripts/check-api-server-env.mjs. Make server config.parse_bool tolerant of shell-wrapped quoted values (e.g. '"true"') and add tests so SMS_AUTH_ENABLED is parsed correctly when shells supply quotes. Update docs to clarify SMS env behaviour, restart requirements, and add guidance + a CSS fallback for old mobile browsers (QQ/X5) so public cover images render even when aspect-ratio is unsupported. Also include related frontend test and component adjustments and add puzzle onboarding handlers/endpoints in server-rs/crates/api-server/src/puzzle.rs.
This commit is contained in:
2026-05-18 23:13:49 +08:00
parent 4c10c181e3
commit d1adfa3406
22 changed files with 4309 additions and 52 deletions

View File

@@ -31,6 +31,7 @@ import {
UserRound,
XCircle,
} from 'lucide-react';
import QRCode from 'qrcode';
import {
type ComponentType,
type CSSProperties,
@@ -42,7 +43,6 @@ import {
useRef,
useState,
} from 'react';
import QRCode from 'qrcode';
import communityQqQrImage from '../../../media/social-media-group/qq.png';
import communityWechatQrImage from '../../../media/social-media-group/wechat.png';
@@ -58,13 +58,13 @@ import type {
ProfileRechargeCenterResponse,
ProfileRechargeProduct,
ProfileReferralInviteCenterResponse,
WechatNativePayment,
ProfileSaveArchiveSummary,
ProfileTaskCenterResponse,
ProfileTaskItem,
ProfileWalletLedgerResponse,
RedeemProfileRewardCodeResponse,
WechatMiniProgramPayParams,
WechatNativePayment,
} from '../../../packages/shared/src/contracts/runtime';
import type { HydratedSavedGameSnapshot } from '../../persistence/runtimeSnapshotTypes';
import { buildPublicWorkDetailUrl } from '../../routing/appPageRoutes';
@@ -137,6 +137,7 @@ import {
resolvePlatformPublicWorkCode,
resolvePlatformWorldCoverImage,
resolvePlatformWorldCoverSlides,
resolvePlatformWorldFallbackCoverImage,
resolvePlatformWorldLeadPortrait,
} from './rpgEntryWorldPresentation';
@@ -392,11 +393,13 @@ function usePlatformDesktopLayout() {
function ResolvedAssetBackdrop({
src,
fallbackSrc,
alt,
className,
ariaHidden = false,
}: {
src?: string | null;
fallbackSrc?: string | null;
alt: string;
className: string;
ariaHidden?: boolean;
@@ -404,6 +407,7 @@ function ResolvedAssetBackdrop({
return (
<ResolvedAssetImage
src={src}
fallbackSrc={fallbackSrc}
alt={alt}
aria-hidden={ariaHidden}
className={className}
@@ -522,6 +526,7 @@ function WorldCard({
variant?: 'standard' | 'immersive';
}) {
const fallbackCoverImage = resolvePlatformWorldCoverImage(entry);
const fallbackAssetCoverImage = resolvePlatformWorldFallbackCoverImage(entry);
const coverSlides = useMemo(() => {
if (!enableCoverCarousel) {
return fallbackCoverImage
@@ -606,6 +611,7 @@ function WorldCard({
{coverImage ? (
<ResolvedAssetBackdrop
src={coverImage}
fallbackSrc={fallbackAssetCoverImage}
alt={entry.worldName}
className="absolute inset-0 h-full w-full object-cover"
/>
@@ -692,6 +698,7 @@ function RecommendCoverOnlyCard({
onClick: () => void;
}) {
const coverImage = resolvePlatformWorldCoverImage(entry);
const fallbackCoverImage = resolvePlatformWorldFallbackCoverImage(entry);
const displayName = formatPlatformWorkDisplayName(entry.worldName);
const typeLabel = describePublicGalleryCardKind(entry);
const authorName = entry.authorDisplayName.trim() || '玩家';
@@ -708,6 +715,7 @@ function RecommendCoverOnlyCard({
{coverImage ? (
<ResolvedAssetBackdrop
src={coverImage}
fallbackSrc={fallbackCoverImage}
alt={entry.worldName}
className="absolute inset-0 h-full w-full object-cover"
/>