Files
what-to-eat/apps/web/app/utils/yunlefunSsoConfig.ts
T
toor 76a5d71252 refactor: 只保留 Web,移除 Capacitor 原生壳
删除 android/、ios/ 两个 Capacitor 原生工程与 capacitor.config.ts、
ionic.config.json,以及 scripts/android、scripts/ios.sh 构建脚本;
根与 apps/web 的 @capacitor/* 依赖和相关 npm scripts 一并移除。

Web 侧去掉原生分支:登录只走云乐坊容器授权面板或顶层重定向,
暗色模式不再改原生状态栏,移除 Android 物理返回键处理。

保留 @nuxtjs/ionic(Web UI 框架)、云乐坊 window.ylf 桥接与 PWA。

决策记录见 docs/decisions/0004-web-only.md。
2026-10-21 12:27:50 +08:00

115 lines
2.8 KiB
TypeScript

import type { RedirectSsoOptions, SsoCodeExchangeOptions } from '@yunlefun/sso'
import {
readSsoClientId,
readSsoRedirectUri,
readSsoScope,
} from '@yunlefun/sso'
export interface YunlefunSsoConfigInput {
clientId?: string
cloudbaseEnv?: string
exchangeUrl?: string
redirectUri?: string
redirectUris?: string | string[]
scope?: string
ssoOrigin?: string
}
export interface YunlefunSsoConfigContext {
currentOrigin: string
}
export interface YunlefunSsoConfig {
cloudbaseEnv: string
exchange: SsoCodeExchangeOptions
redirect: RedirectSsoOptions
}
export type YunlefunSsoConfigResult
= | { ok: true, config: YunlefunSsoConfig }
| { ok: false, message: string }
const CLOUDBASE_ENV_RE = /^[a-z0-9][a-z0-9-]{0,63}$/i
const TRAILING_SLASH_RE = /\/+$/
export function resolveYunlefunSsoConfig(
input: YunlefunSsoConfigInput,
context: YunlefunSsoConfigContext,
): YunlefunSsoConfigResult {
const clientId = readSsoClientId(input.clientId)
const cloudbaseEnv = normalizeValue(input.cloudbaseEnv)
const exchangeUrl = readSsoRedirectUri(input.exchangeUrl)
const webRedirectUris = readWebRedirectUris(input.redirectUris ?? input.redirectUri)
const redirectUri = webRedirectUris.find(uri => new URL(uri).origin === context.currentOrigin)
const scope = readSsoScope(input.scope)
const ssoOrigin = normalizeHttpsOrigin(input.ssoOrigin)
if (!clientId
|| !CLOUDBASE_ENV_RE.test(cloudbaseEnv)
|| !exchangeUrl
|| webRedirectUris.length === 0
|| scope.length === 0
|| !ssoOrigin) {
return {
ok: false,
message: '登录服务尚未完成 SSO v3 配置',
}
}
if (!redirectUri) {
return {
ok: false,
message: '当前域名尚未登记为登录回跳地址',
}
}
return {
ok: true,
config: {
cloudbaseEnv,
exchange: { exchangeUrl },
redirect: {
clientId,
redirectUri,
scope,
ssoOrigin,
},
},
}
}
function readWebRedirectUris(value: string | string[] | undefined): string[] {
const candidates = Array.isArray(value)
? value
: typeof value === 'string'
? value.split(/[\n,]/)
: []
return [...new Set(candidates
.map(candidate => readSsoRedirectUri(candidate.trim()))
.filter((candidate): candidate is string => Boolean(candidate)))]
}
function normalizeHttpsOrigin(value: string | undefined): string {
const normalized = normalizeValue(value).replace(TRAILING_SLASH_RE, '')
if (!normalized)
return ''
try {
const url = new URL(normalized)
return url.protocol === 'https:'
&& !url.username
&& !url.password
&& url.origin === normalized
? url.origin
: ''
}
catch {
return ''
}
}
function normalizeValue(value: string | undefined): string {
return typeof value === 'string' ? value.trim() : ''
}