fix web lint

This commit is contained in:
yunlu.wen
2026-05-22 18:23:56 +08:00
parent 102a9f3eb3
commit 0f39ac8960
3 changed files with 56 additions and 32 deletions

View File

@ -93,12 +93,6 @@ catalog:
'@napi-rs/keyring': 1.1.6
'@next/eslint-plugin-next': 16.2.6
'@next/mdx': 16.2.6
'@oclif/core': 4.11.1
'@oclif/plugin-autocomplete': 3.2.6
'@oclif/plugin-help': 6.2.10
'@oclif/plugin-not-found': 3.2.18
'@oclif/plugin-version': 2.2.16
'@oclif/plugin-warn-if-update-available': 3.1.13
'@orpc/client': 1.14.3
'@orpc/contract': 1.14.3
'@orpc/openapi-client': 1.14.3
@ -212,7 +206,6 @@ catalog:
next: 16.2.6
next-themes: 0.4.6
nuqs: 2.8.9
oclif: 4.15.5
open: 10.1.0
ora: 8.1.0
picocolors: 1.1.0

View File

@ -18,14 +18,18 @@ type OAuthPendingRedirect = {
const getCurrentUnixTimestamp = () => Math.floor(Date.now() / 1000)
function validate(target: string): string | null {
if (typeof window === 'undefined') return null
if (typeof window === 'undefined')
return null
try {
const url = new URL(target, window.location.origin)
if (url.origin !== window.location.origin) return null
if (url.origin !== window.location.origin)
return null
const allowedKeys = ALLOWED[url.pathname]
if (!allowedKeys) return null
if (!allowedKeys)
return null
for (const key of url.searchParams.keys()) {
if (!allowedKeys.has(key)) return null
if (!allowedKeys.has(key))
return null
}
return url.pathname + (url.search || '')
}
@ -39,13 +43,18 @@ function validate(target: string): string | null {
// /device tabs don't clobber each other. 15-min TTL drops stale values.
// Same-origin + exact-path whitelist prevents open-redirect.
export const setPostLoginRedirect = (value: string | null) => {
if (typeof window === 'undefined') return
if (typeof window === 'undefined')
return
if (value === null) {
try { sessionStorage.removeItem(DEVICE_REDIRECT_KEY) } catch {}
try {
sessionStorage.removeItem(DEVICE_REDIRECT_KEY)
}
catch {}
return
}
const safe = validate(value)
if (!safe) return
if (!safe)
return
try {
sessionStorage.setItem(DEVICE_REDIRECT_KEY, JSON.stringify({ target: safe, ts: Date.now() }))
}
@ -53,7 +62,8 @@ export const setPostLoginRedirect = (value: string | null) => {
}
function getDeviceRedirect(): string | null {
if (typeof window === 'undefined') return null
if (typeof window === 'undefined')
return null
let raw: string | null = null
try {
raw = sessionStorage.getItem(DEVICE_REDIRECT_KEY)
@ -62,11 +72,14 @@ function getDeviceRedirect(): string | null {
catch {
return null
}
if (!raw) return null
if (!raw)
return null
try {
const parsed = JSON.parse(raw)
if (typeof parsed?.target !== 'string' || typeof parsed?.ts !== 'number') return null
if (Date.now() - parsed.ts > DEVICE_TTL_MS) return null
if (typeof parsed?.target !== 'string' || typeof parsed?.ts !== 'number')
return null
if (Date.now() - parsed.ts > DEVICE_TTL_MS)
return null
return validate(parsed.target)
}
catch {
@ -124,6 +137,7 @@ export const resolvePostLoginRedirect = (searchParams?: ReadonlyURLSearchParams)
}
}
const device = getDeviceRedirect()
if (device) return device
if (device)
return device
return getOAuthPendingRedirect()
}

View File

@ -22,9 +22,14 @@ const DEVICE_BASE = '/openapi/v1/oauth/device'
// switches on `code` to choose user-facing copy / view; never render
// `status` or raw body to the user.
export class DeviceFlowError extends Error {
constructor(public code: string, public status: number) {
code: string
status: number
constructor(code: string, status: number) {
super(code)
this.name = 'DeviceFlowError'
this.code = code
this.status = status
}
}
@ -35,7 +40,8 @@ async function failFromResponse(res: Response): Promise<never> {
let serverCode = ''
try {
const body = await res.clone().json()
if (body && typeof body.error === 'string') serverCode = body.error
if (body && typeof body.error === 'string')
serverCode = body.error
}
catch { /* non-JSON body — fall through to status mapping */ }
@ -44,12 +50,18 @@ async function failFromResponse(res: Response): Promise<never> {
}
function statusFallbackCode(status: number): string {
if (status === 429) return 'rate_limited'
if (status === 401) return 'no_session'
if (status === 403) return 'forbidden'
if (status === 404) return 'not_found'
if (status === 409) return 'conflict'
if (status >= 500) return 'server_error'
if (status === 429)
return 'rate_limited'
if (status === 401)
return 'no_session'
if (status === 403)
return 'forbidden'
if (status === 404)
return 'not_found'
if (status === 409)
return 'conflict'
if (status >= 500)
return 'server_error'
return 'unknown'
}
@ -69,7 +81,8 @@ export async function deviceLookup(user_code: string): Promise<DeviceLookupReply
const res = await fetch(`${DEVICE_BASE}/lookup?user_code=${encodeURIComponent(user_code)}`, {
method: 'GET',
})
if (!res.ok) await failFromResponse(res)
if (!res.ok)
await failFromResponse(res)
return res.json()
}
@ -83,7 +96,8 @@ export async function deviceApproveAccount(user_code: string): Promise<{ status:
},
body: JSON.stringify({ user_code }),
})
if (!res.ok) await failFromResponse(res)
if (!res.ok)
await failFromResponse(res)
return res.json()
}
@ -97,7 +111,8 @@ export async function deviceDenyAccount(user_code: string): Promise<{ status: 'd
},
body: JSON.stringify({ user_code }),
})
if (!res.ok) await failFromResponse(res)
if (!res.ok)
await failFromResponse(res)
return res.json()
}
@ -116,7 +131,8 @@ export async function fetchApprovalContext(): Promise<ApprovalContext> {
method: 'GET',
credentials: 'include',
})
if (!res.ok) await failFromResponse(res)
if (!res.ok)
await failFromResponse(res)
return res.json()
}
@ -130,5 +146,6 @@ export async function approveExternal(ctx: ApprovalContext, user_code: string):
},
body: JSON.stringify({ user_code }),
})
if (!res.ok) await failFromResponse(res)
if (!res.ok)
await failFromResponse(res)
}