동의 게이트와 런타임 안정화
This commit is contained in:
parent
0eb7d925ed
commit
0ec266a761
34 changed files with 1186 additions and 158 deletions
|
|
@ -156,6 +156,30 @@ export interface paths {
|
|||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/auth/consent": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
/**
|
||||
* Accept Consent
|
||||
* @description Record the current learner's practice-session consent receipt.
|
||||
*/
|
||||
post: operations["accept_consent_auth_consent_post"];
|
||||
/**
|
||||
* Withdraw Consent
|
||||
* @description Withdraw practice-session consent until the learner accepts again.
|
||||
*/
|
||||
delete: operations["withdraw_consent_auth_consent_delete"];
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/auth/dev-login": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
|
@ -1072,6 +1096,19 @@ export interface components {
|
|||
/** Rationale */
|
||||
rationale?: string | null;
|
||||
};
|
||||
/** ConsentRequest */
|
||||
ConsentRequest: {
|
||||
/**
|
||||
* Accepted
|
||||
* @default true
|
||||
*/
|
||||
accepted: boolean;
|
||||
};
|
||||
/** ConsentResponse */
|
||||
ConsentResponse: {
|
||||
/** Consent At */
|
||||
consent_at?: number | null;
|
||||
};
|
||||
/** CrisisResourceResponse */
|
||||
CrisisResourceResponse: {
|
||||
/** Message */
|
||||
|
|
@ -1294,6 +1331,8 @@ export interface components {
|
|||
MeResponse: {
|
||||
/** Cohort Ids */
|
||||
cohort_ids: string[];
|
||||
/** Consent At */
|
||||
consent_at?: number | null;
|
||||
/** Display Name */
|
||||
display_name: string;
|
||||
/** Email */
|
||||
|
|
@ -2586,6 +2625,74 @@ export interface operations {
|
|||
};
|
||||
};
|
||||
};
|
||||
accept_consent_auth_consent_post: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: {
|
||||
"__Host-vignette_sid"?: string | null;
|
||||
vignette_sid?: string | null;
|
||||
};
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["ConsentRequest"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ConsentResponse"];
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["HTTPValidationError"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
withdraw_consent_auth_consent_delete: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: {
|
||||
"__Host-vignette_sid"?: string | null;
|
||||
vignette_sid?: string | null;
|
||||
};
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ConsentResponse"];
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["HTTPValidationError"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
dev_login_auth_dev_login_post: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
|
|
|||
|
|
@ -161,6 +161,11 @@ export interface MeResponse {
|
|||
display_name: string;
|
||||
role: string; // "learner" | "teacher" | "admin"
|
||||
cohort_ids: string[];
|
||||
consent_at: number | null;
|
||||
}
|
||||
|
||||
export interface ConsentResponse {
|
||||
consent_at: number | null;
|
||||
}
|
||||
|
||||
export interface AuthConfigResponse {
|
||||
|
|
@ -179,6 +184,8 @@ export interface AuthConfigResponse {
|
|||
|
||||
export const authApi = {
|
||||
config: () => api.get<AuthConfigResponse>("/auth/config"),
|
||||
acceptConsent: () => api.post<ConsentResponse>("/auth/consent", { accepted: true }),
|
||||
withdrawConsent: () => api.del<ConsentResponse>("/auth/consent"),
|
||||
};
|
||||
|
||||
export type SessionStage = "라포" | "탐색" | "개입" | "정리";
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
useState,
|
||||
type ReactNode,
|
||||
} from "react";
|
||||
import { api, type MeResponse } from "./api";
|
||||
import { api, authApi, type MeResponse } from "./api";
|
||||
|
||||
export type Role = "learner" | "teacher" | "admin";
|
||||
export type DesignRole = "learner" | "instructor" | "admin";
|
||||
|
|
@ -18,6 +18,7 @@ export interface AuthUser {
|
|||
name: string;
|
||||
role: Role;
|
||||
cohortIds: string[];
|
||||
consentAt: number | null;
|
||||
}
|
||||
|
||||
export interface AuthContextValue {
|
||||
|
|
@ -26,6 +27,8 @@ export interface AuthContextValue {
|
|||
loading: boolean;
|
||||
login: (role: Role, opts?: { email?: string; displayName?: string }) => Promise<AuthUser>;
|
||||
logout: () => Promise<void>;
|
||||
acceptConsent: () => Promise<void>;
|
||||
withdrawConsent: () => Promise<void>;
|
||||
}
|
||||
|
||||
export function designRoleOf(role: Role): DesignRole {
|
||||
|
|
@ -75,6 +78,7 @@ function userFromMe(me: MeResponse): AuthUser {
|
|||
name: me.display_name || me.email || me.user_id,
|
||||
role: (me.role as Role) ?? "learner",
|
||||
cohortIds: me.cohort_ids ?? [],
|
||||
consentAt: me.consent_at ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -124,9 +128,21 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|||
}
|
||||
}, []);
|
||||
|
||||
const acceptConsent = useCallback<AuthContextValue["acceptConsent"]>(async () => {
|
||||
const response = await authApi.acceptConsent();
|
||||
setUser((current) =>
|
||||
current ? { ...current, consentAt: response.consent_at ?? null } : current,
|
||||
);
|
||||
}, []);
|
||||
|
||||
const withdrawConsent = useCallback<AuthContextValue["withdrawConsent"]>(async () => {
|
||||
await authApi.withdrawConsent();
|
||||
setUser((current) => (current ? { ...current, consentAt: null } : current));
|
||||
}, []);
|
||||
|
||||
const value = useMemo<AuthContextValue>(
|
||||
() => ({ user, role: user?.role ?? null, loading, login, logout }),
|
||||
[user, loading, login, logout],
|
||||
() => ({ user, role: user?.role ?? null, loading, login, logout, acceptConsent, withdrawConsent }),
|
||||
[user, loading, login, logout, acceptConsent, withdrawConsent],
|
||||
);
|
||||
|
||||
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue