deploy configuration
This commit is contained in:
parent
9ba3c47d82
commit
dac02c61c3
45 changed files with 1405 additions and 582 deletions
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "cms-ui",
|
||||
"name": "go-tell-ui",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ export async function requestSession(): Promise<SessionResponse | null> {
|
|||
}
|
||||
|
||||
function demoAuthHeaders(): Record<string, string> {
|
||||
const token = window.sessionStorage.getItem('cms-token')
|
||||
const token = window.sessionStorage.getItem('go-tell-token')
|
||||
return token ? {Authorization: `Bearer ${token}`} : {}
|
||||
}
|
||||
|
||||
|
|
@ -99,7 +99,7 @@ async function contentRequest<T>(path = '', init?: RequestInit): Promise<T> {
|
|||
headers: { Accept: 'application/json', ...demoAuthHeaders(), ...(init?.body ? { 'Content-Type': 'application/json' } : {}), ...init?.headers },
|
||||
})
|
||||
if (!response.ok) {
|
||||
if (response.status === 401) throw new Error('Your SSO session is required to edit content.')
|
||||
if (response.status === 401) throw new Error('You have to sign in to edit content.')
|
||||
throw new Error((await response.text()) || 'Unable to load content.')
|
||||
}
|
||||
return (await response.json()) as T
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import { createContext, useCallback, useContext, useEffect, useMemo, useState, type ReactNode } from 'react'
|
||||
import { apiConfig, requestSession, signInWithGoogle, sudoLogin } from './api'
|
||||
import { apiConfig, apiUrl, requestSession, signInWithGoogle, sudoLogin } from './api'
|
||||
|
||||
// There is no sign-in or sign-out: the upstream SSO proxy decides whether the
|
||||
// current request carries the owner's identity. Signing in just sends the
|
||||
// browser to the configured Google/IdP URL.
|
||||
// Signing in is either the Go Loose browser login or a redirect to the
|
||||
// identity-aware proxy, depending on what the API advertises: /api/auth/session
|
||||
// returns loginUrl and logoutUrl when Go Loose is configured. The demo profile
|
||||
// keeps using its own token in sessionStorage.
|
||||
type AuthState = {
|
||||
loading: boolean
|
||||
authenticated: boolean
|
||||
|
|
@ -12,6 +13,7 @@ type AuthState = {
|
|||
error: string | null
|
||||
refresh: () => Promise<void>
|
||||
canSignIn: boolean
|
||||
canSignOut: boolean
|
||||
signIn: () => void
|
||||
sudo: (username: string, password: string) => Promise<void>
|
||||
signOut: () => void
|
||||
|
|
@ -23,6 +25,8 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|||
const [authenticated, setAuthenticated] = useState(false)
|
||||
const [canEdit, setCanEdit] = useState(false)
|
||||
const [username, setUsername] = useState<string | undefined>()
|
||||
const [loginUrl, setLoginUrl] = useState('')
|
||||
const [logoutUrl, setLogoutUrl] = useState('')
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
|
|
@ -33,10 +37,12 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|||
setAuthenticated(session?.authenticated === true)
|
||||
setCanEdit(session?.canEdit === true)
|
||||
setUsername(session?.username)
|
||||
setLoginUrl(session?.loginUrl ?? '')
|
||||
setLogoutUrl(session?.logoutUrl ?? '')
|
||||
} catch (err) {
|
||||
setAuthenticated(false)
|
||||
setCanEdit(false)
|
||||
setError(err instanceof Error ? err.message : 'Unable to check your SSO session.')
|
||||
setError(err instanceof Error ? err.message : 'Unable to check your session.')
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
|
|
@ -44,25 +50,36 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|||
|
||||
useEffect(() => { void refresh() }, [refresh])
|
||||
|
||||
const signIn = useCallback(() => { signInWithGoogle() }, [])
|
||||
const signIn = useCallback(() => {
|
||||
if (loginUrl) {
|
||||
window.location.assign(apiUrl(loginUrl))
|
||||
return
|
||||
}
|
||||
signInWithGoogle()
|
||||
}, [loginUrl])
|
||||
const sudo = useCallback(async (username: string, password: string) => {
|
||||
const result = await sudoLogin(username, password)
|
||||
window.sessionStorage.setItem('cms-token', result.token)
|
||||
window.sessionStorage.setItem('go-tell-token', result.token)
|
||||
setAuthenticated(true)
|
||||
setCanEdit(true)
|
||||
setUsername(result.user.username)
|
||||
}, [])
|
||||
const signOut = useCallback(() => {
|
||||
window.sessionStorage.removeItem('cms-token')
|
||||
if (logoutUrl) {
|
||||
window.location.assign(apiUrl(logoutUrl))
|
||||
return
|
||||
}
|
||||
window.sessionStorage.removeItem('go-tell-token')
|
||||
setAuthenticated(false)
|
||||
setCanEdit(false)
|
||||
setUsername(undefined)
|
||||
}, [])
|
||||
const canSignIn = apiConfig.signIn !== ''
|
||||
}, [logoutUrl])
|
||||
const canSignIn = apiConfig.signIn !== '' || loginUrl !== ''
|
||||
const canSignOut = logoutUrl !== ''
|
||||
|
||||
const value = useMemo(
|
||||
() => ({ loading, authenticated, canEdit, username, error, refresh, canSignIn, signIn, sudo, signOut }),
|
||||
[loading, authenticated, canEdit, username, error, refresh, canSignIn, signIn, sudo, signOut],
|
||||
() => ({ loading, authenticated, canEdit, username, error, refresh, canSignIn, canSignOut, signIn, sudo, signOut }),
|
||||
[loading, authenticated, canEdit, username, error, refresh, canSignIn, canSignOut, signIn, sudo, signOut],
|
||||
)
|
||||
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {type DemoUser} from '../models'
|
|||
import {SudoTerminal} from './SudoTerminal'
|
||||
|
||||
export function Shell({children}: {children: ReactNode}) {
|
||||
const {authenticated, loading, canSignIn, signIn, username, sudo} = useAuth()
|
||||
const {authenticated, loading, canSignIn, canSignOut, signIn, signOut, username, sudo} = useAuth()
|
||||
const [sudoOpen, setSudoOpen] = useState(false)
|
||||
const [helpOpen, setHelpOpen] = useState(false)
|
||||
const [users, setUsers] = useState<DemoUser[]>([])
|
||||
|
|
@ -24,7 +24,10 @@ export function Shell({children}: {children: ReactNode}) {
|
|||
<span className="brand-cursor">_</span><small>linux · go · java · ideas</small></Link>
|
||||
<div className="header-actions">
|
||||
{!loading && <button className="mode-badge" type="button" onClick={() => {
|
||||
if (authenticated) return
|
||||
if (authenticated) {
|
||||
if (canSignOut) signOut()
|
||||
return
|
||||
}
|
||||
if (users.length > 0) setSudoOpen(true)
|
||||
else if (canSignIn) signIn()
|
||||
}}>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,12 @@ export type SessionResponse = {
|
|||
authenticated?: boolean
|
||||
canEdit?: boolean
|
||||
username?: string
|
||||
email?: string
|
||||
displayName?: string
|
||||
// Go Loose advertises its own login and logout endpoints; the UI follows them
|
||||
// instead of sending the browser to a single-sign-on proxy.
|
||||
loginUrl?: string
|
||||
logoutUrl?: string
|
||||
}
|
||||
|
||||
export type DemoUser = { username: string; name: string }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue