feat: send xsrf_token header
Some checks failed
Build and Deploy Go App / build (push) Has been cancelled
Build and Deploy Go App / deploy (push) Has been cancelled

This commit is contained in:
nihonium 2025-12-04 07:17:31 +03:00
parent b79a6b9117
commit 1bbfa338d9
Signed by: nihonium
GPG key ID: 0251623741027CFC
15 changed files with 151 additions and 27 deletions

View file

@ -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';

View 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;

View 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;

View 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;

View file

@ -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`,

View file

@ -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: {

View file

@ -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 {

View file

@ -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) {