Новый сервис pcp-tgu-ui-service: ТГУ-фронт на headless-auth nstart + платформенный CI/деплой

This commit is contained in:
Дмитрий Соловьев
2026-06-09 13:35:13 +03:00
parent 0b617594fd
commit 505bf1f31c
129 changed files with 21640 additions and 0 deletions
@@ -0,0 +1,74 @@
import { createAppAuth, type DefaultAuthUser } from '@nstart/auth'
import { createContext, type ReactNode, useContext, useEffect } from 'react'
import { setApiAuthToken } from '../api/apiFetch'
import { env } from '../config/env'
export type AppUser = DefaultAuthUser
// Состояние, совместимое с AuthState из @nstart/auth (headless-ядро). Используем
// ТОЛЬКО headless-слой пакета: createAppAuth/useAppAuth дают токен, login, logout,
// loading и коды ошибок — без единого компонента @nstart/ui (их UI: ProfilePage/
// UserPanel — намеренно не подключаем).
interface AppAuthState {
user: AppUser | null
token: string | null
loading: boolean
errorCode: 'notAuthenticated' | 'initFailed' | null
errorDetails: string | null
login: () => void
logout: () => void
}
// Anonymous-режим: фейковый контекст, чтобы useAppAuth() работал везде в коде
// без условий. Токен null → apiFetch не добавляет Authorization, поведение как у
// исходного сервиса (открывается сразу, без Keycloak).
const ANONYMOUS_STATE: AppAuthState = {
user: null,
token: null,
loading: false,
errorCode: null,
errorDetails: null,
login: () => undefined,
logout: () => undefined,
}
const AnonymousAuthContext = createContext<AppAuthState>(ANONYMOUS_STATE)
function AnonymousAuthProvider({ children }: { children: ReactNode }) {
return (
<AnonymousAuthContext.Provider value={ANONYMOUS_STATE}>
{children}
</AnonymousAuthContext.Provider>
)
}
function useAnonymousAuth(): AppAuthState {
return useContext(AnonymousAuthContext)
}
const newStartIdAuth = createAppAuth({
url: env.newStartIdUrl,
realm: env.newStartIdRealm,
clientId: env.newStartIdClientId,
loginMode: 'redirect',
})
export const { AppAuthProvider, useAppAuth } =
env.authMode === 'new-start-id'
? newStartIdAuth
: { AppAuthProvider: AnonymousAuthProvider, useAppAuth: useAnonymousAuth }
/**
* Прокидывает токен из useAppAuth() в сетевой слой (держатель apiFetch). Ничего
* не рендерит; монтируется внутри AppAuthProvider. В режиме anonymous token === null,
* поэтому заголовок Authorization не выставляется и поведение запросов не меняется.
*
* @returns Ничего не отображает (null).
*/
export function AuthTokenBridge(): null {
const { token } = useAppAuth()
useEffect(() => {
setApiAuthToken(token ?? null)
}, [token])
return null
}