feat: send xsrf_token header
This commit is contained in:
parent
b79a6b9117
commit
1bbfa338d9
15 changed files with 151 additions and 27 deletions
|
|
@ -7,6 +7,9 @@ export { CancelablePromise, CancelError } from './core/CancelablePromise';
|
|||
export { OpenAPI } from './core/OpenAPI';
|
||||
export type { OpenAPIConfig } from './core/OpenAPI';
|
||||
|
||||
export type { accessToken } from './models/accessToken';
|
||||
export type { csrfToken } from './models/csrfToken';
|
||||
export type { csrfTokenHeader } from './models/csrfTokenHeader';
|
||||
export type { cursor } from './models/cursor';
|
||||
export type { CursorObj } from './models/CursorObj';
|
||||
export type { Image } from './models/Image';
|
||||
|
|
|
|||
9
modules/frontend/src/api/models/accessToken.ts
Normal file
9
modules/frontend/src/api/models/accessToken.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* JWT access token.
|
||||
*
|
||||
*/
|
||||
export type accessToken = string;
|
||||
11
modules/frontend/src/api/models/csrfToken.ts
Normal file
11
modules/frontend/src/api/models/csrfToken.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Anti-CSRF token (Double Submit Cookie pattern).
|
||||
* Stored in non-HttpOnly cookie, readable by JavaScript.
|
||||
* Must be echoed in `X-XSRF-TOKEN` header for state-changing requests (POST/PUT/PATCH/DELETE).
|
||||
*
|
||||
*/
|
||||
export type csrfToken = string;
|
||||
10
modules/frontend/src/api/models/csrfTokenHeader.ts
Normal file
10
modules/frontend/src/api/models/csrfTokenHeader.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Anti-CSRF token. Must match the `XSRF-TOKEN` cookie.
|
||||
* Required for all state-changing requests (POST/PUT/PATCH/DELETE).
|
||||
*
|
||||
*/
|
||||
export type csrfTokenHeader = string;
|
||||
|
|
@ -135,12 +135,16 @@ export class DefaultService {
|
|||
* Password updates must be done via the dedicated auth-service (`/auth/`).
|
||||
* Fields not provided in the request body remain unchanged.
|
||||
*
|
||||
* @param xXsrfToken Anti-CSRF token. Must match the `XSRF-TOKEN` cookie.
|
||||
* Required for all state-changing requests (POST/PUT/PATCH/DELETE).
|
||||
*
|
||||
* @param userId User ID (primary key)
|
||||
* @param requestBody
|
||||
* @returns User User updated successfully. Returns updated user representation (excluding sensitive fields).
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static updateUser(
|
||||
xXsrfToken: string,
|
||||
userId: number,
|
||||
requestBody: {
|
||||
/**
|
||||
|
|
@ -171,6 +175,9 @@ export class DefaultService {
|
|||
path: {
|
||||
'user_id': userId,
|
||||
},
|
||||
headers: {
|
||||
'X-XSRF-TOKEN': xXsrfToken,
|
||||
},
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
|
|
@ -309,6 +316,9 @@ export class DefaultService {
|
|||
/**
|
||||
* Update a usertitle
|
||||
* User updating title list of watched
|
||||
* @param xXsrfToken Anti-CSRF token. Must match the `XSRF-TOKEN` cookie.
|
||||
* Required for all state-changing requests (POST/PUT/PATCH/DELETE).
|
||||
*
|
||||
* @param userId
|
||||
* @param titleId
|
||||
* @param requestBody
|
||||
|
|
@ -316,6 +326,7 @@ export class DefaultService {
|
|||
* @throws ApiError
|
||||
*/
|
||||
public static updateUserTitle(
|
||||
xXsrfToken: string,
|
||||
userId: number,
|
||||
titleId: number,
|
||||
requestBody: {
|
||||
|
|
@ -330,6 +341,9 @@ export class DefaultService {
|
|||
'user_id': userId,
|
||||
'title_id': titleId,
|
||||
},
|
||||
headers: {
|
||||
'X-XSRF-TOKEN': xXsrfToken,
|
||||
},
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
|
|
@ -344,12 +358,16 @@ export class DefaultService {
|
|||
/**
|
||||
* Delete a usertitle
|
||||
* User deleting title from list of watched
|
||||
* @param xXsrfToken Anti-CSRF token. Must match the `XSRF-TOKEN` cookie.
|
||||
* Required for all state-changing requests (POST/PUT/PATCH/DELETE).
|
||||
*
|
||||
* @param userId
|
||||
* @param titleId
|
||||
* @returns any Title successfully deleted
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static deleteUserTitle(
|
||||
xXsrfToken: string,
|
||||
userId: number,
|
||||
titleId: number,
|
||||
): CancelablePromise<any> {
|
||||
|
|
@ -360,6 +378,9 @@ export class DefaultService {
|
|||
'user_id': userId,
|
||||
'title_id': titleId,
|
||||
},
|
||||
headers: {
|
||||
'X-XSRF-TOKEN': xXsrfToken,
|
||||
},
|
||||
errors: {
|
||||
401: `Unauthorized — missing or invalid auth token`,
|
||||
403: `Forbidden — user not allowed to delete title`,
|
||||
|
|
|
|||
|
|
@ -12,19 +12,17 @@ export class AuthService {
|
|||
* @returns any Sign-up result
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static postAuthSignUp(
|
||||
public static postSignUp(
|
||||
requestBody: {
|
||||
nickname: string;
|
||||
pass: string;
|
||||
},
|
||||
): CancelablePromise<{
|
||||
success?: boolean;
|
||||
error?: string | null;
|
||||
user_id?: string | null;
|
||||
user_id: number;
|
||||
}> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/auth/sign-up',
|
||||
url: '/sign-up',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
});
|
||||
|
|
@ -35,19 +33,18 @@ export class AuthService {
|
|||
* @returns any Sign-in result with JWT
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static postAuthSignIn(
|
||||
public static postSignIn(
|
||||
requestBody: {
|
||||
nickname: string;
|
||||
pass: string;
|
||||
},
|
||||
): CancelablePromise<{
|
||||
error?: string | null;
|
||||
user_id?: string | null;
|
||||
user_name?: string | null;
|
||||
user_id: number;
|
||||
user_name: string;
|
||||
}> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/auth/sign-in',
|
||||
url: '/sign-in',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { DefaultService } from "../../api";
|
||||
import type { UserTitleStatus } from "../../api";
|
||||
import { useCookies } from 'react-cookie';
|
||||
|
||||
import {
|
||||
ClockIcon,
|
||||
CheckCircleIcon,
|
||||
|
|
@ -17,6 +19,9 @@ const STATUS_BUTTONS: { status: UserTitleStatus; icon: React.ReactNode; label: s
|
|||
];
|
||||
|
||||
export function TitleStatusControls({ titleId }: { titleId: number }) {
|
||||
const [cookies] = useCookies(['xsrf_token']);
|
||||
const xsrfToken = cookies['xsrf_token'] || null;
|
||||
|
||||
const [currentStatus, setCurrentStatus] = useState<UserTitleStatus | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
|
|
@ -41,7 +46,7 @@ export function TitleStatusControls({ titleId }: { titleId: number }) {
|
|||
try {
|
||||
// 1) Если кликнули на текущий статус — DELETE
|
||||
if (currentStatus === status) {
|
||||
await DefaultService.deleteUserTitle(userId, titleId);
|
||||
await DefaultService.deleteUserTitle(xsrfToken, userId, titleId);
|
||||
setCurrentStatus(null);
|
||||
return;
|
||||
}
|
||||
|
|
@ -56,7 +61,7 @@ export function TitleStatusControls({ titleId }: { titleId: number }) {
|
|||
setCurrentStatus(added.status);
|
||||
} else {
|
||||
// уже есть запись — PATCH
|
||||
const updated = await DefaultService.updateUserTitle(userId, titleId, { status });
|
||||
const updated = await DefaultService.updateUserTitle(xsrfToken, userId, titleId, { status });
|
||||
setCurrentStatus(updated.status);
|
||||
}
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -17,23 +17,23 @@ export const LoginPage: React.FC = () => {
|
|||
|
||||
try {
|
||||
if (isLogin) {
|
||||
const res = await AuthService.postAuthSignIn({ nickname, pass: password });
|
||||
const res = await AuthService.postSignIn({ nickname, pass: password });
|
||||
if (res.user_id && res.user_name) {
|
||||
// Сохраняем user_id и username в localStorage
|
||||
localStorage.setItem("userId", res.user_id);
|
||||
localStorage.setItem("userId", res.user_id.toString());
|
||||
localStorage.setItem("username", res.user_name);
|
||||
|
||||
navigate("/profile"); // редирект на профиль
|
||||
} else {
|
||||
setError(res.error || "Login failed");
|
||||
setError("Login failed");
|
||||
}
|
||||
} else {
|
||||
// SignUp оставляем без сохранения данных
|
||||
const res = await AuthService.postAuthSignUp({ nickname, pass: password });
|
||||
const res = await AuthService.postSignUp({ nickname, pass: password });
|
||||
if (res.user_id) {
|
||||
setIsLogin(true); // переключаемся на login после регистрации
|
||||
} else {
|
||||
setError(res.error || "Sign up failed");
|
||||
setError("Sign up failed");
|
||||
}
|
||||
}
|
||||
} catch (err: any) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue