0.5.8-panel-upgrade-community-crud-fix
All checks were successful
Deploy on push / deploy (push) Successful in 6s
All checks were successful
Deploy on push / deploy (push) Successful in 6s
This commit is contained in:
139
panel/graphql/index.ts
Normal file
139
panel/graphql/index.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* API-клиент для работы с GraphQL
|
||||
* @module api
|
||||
*/
|
||||
|
||||
import {
|
||||
AUTH_TOKEN_KEY,
|
||||
clearAuthTokens,
|
||||
getAuthTokenFromCookie,
|
||||
getCsrfTokenFromCookie
|
||||
} from '../utils/auth'
|
||||
|
||||
/**
|
||||
* Тип для произвольных данных GraphQL
|
||||
*/
|
||||
type GraphQLData = Record<string, unknown>
|
||||
|
||||
/**
|
||||
* Возвращает заголовки для GraphQL запроса с учетом авторизации и CSRF
|
||||
* @returns Объект с заголовками
|
||||
*/
|
||||
function getRequestHeaders(): Record<string, string> {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json'
|
||||
}
|
||||
|
||||
// Проверяем наличие токена в localStorage
|
||||
const localToken = localStorage.getItem(AUTH_TOKEN_KEY)
|
||||
|
||||
// Проверяем наличие токена в cookie
|
||||
const cookieToken = getAuthTokenFromCookie()
|
||||
|
||||
// Используем токен из localStorage или cookie
|
||||
const token = localToken || cookieToken
|
||||
|
||||
// Если есть токен, добавляем его в заголовок Authorization с префиксом Bearer
|
||||
if (token && token.length > 10) {
|
||||
headers['Authorization'] = `Bearer ${token}`
|
||||
console.debug('Отправка запроса с токеном авторизации')
|
||||
}
|
||||
|
||||
// Добавляем CSRF-токен, если он есть
|
||||
const csrfToken = getCsrfTokenFromCookie()
|
||||
if (csrfToken) {
|
||||
headers['X-CSRF-Token'] = csrfToken
|
||||
console.debug('Добавлен CSRF-токен в запрос')
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет GraphQL запрос
|
||||
* @param endpoint - URL эндпоинта GraphQL
|
||||
* @param query - GraphQL запрос
|
||||
* @param variables - Переменные запроса
|
||||
* @returns Результат запроса
|
||||
*/
|
||||
export async function query<T = unknown>(
|
||||
endpoint: string,
|
||||
query: string,
|
||||
variables?: Record<string, unknown>
|
||||
): Promise<T> {
|
||||
try {
|
||||
console.log(`[GraphQL] Making request to ${endpoint}`)
|
||||
console.log(`[GraphQL] Query: ${query.substring(0, 100)}...`)
|
||||
|
||||
const response = await fetch(endpoint, {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({
|
||||
query,
|
||||
variables
|
||||
})
|
||||
})
|
||||
|
||||
console.log(`[GraphQL] Response status: ${response.status}`)
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 401) {
|
||||
console.log('[GraphQL] Unauthorized response, clearing auth tokens')
|
||||
clearAuthTokens()
|
||||
// Перенаправляем на страницу входа только если мы не на ней
|
||||
if (!window.location.pathname.includes('/login')) {
|
||||
window.location.href = '/login'
|
||||
}
|
||||
}
|
||||
const errorText = await response.text()
|
||||
throw new Error(`HTTP error: ${response.status} ${errorText}`)
|
||||
}
|
||||
|
||||
const result = await response.json()
|
||||
console.log('[GraphQL] Response received:', result)
|
||||
|
||||
if (result.errors) {
|
||||
// Проверяем ошибки авторизации
|
||||
const hasUnauthorized = result.errors.some(
|
||||
(error: { message?: string }) =>
|
||||
error.message?.toLowerCase().includes('unauthorized') ||
|
||||
error.message?.toLowerCase().includes('please login')
|
||||
)
|
||||
|
||||
if (hasUnauthorized) {
|
||||
console.log('[GraphQL] Unauthorized error in response, clearing auth tokens')
|
||||
clearAuthTokens()
|
||||
// Перенаправляем на страницу входа только если мы не на ней
|
||||
if (!window.location.pathname.includes('/login')) {
|
||||
window.location.href = '/login'
|
||||
}
|
||||
}
|
||||
|
||||
// Handle GraphQL errors
|
||||
const errorMessage = result.errors.map((e: { message?: string }) => e.message).join(', ')
|
||||
throw new Error(`GraphQL error: ${errorMessage}`)
|
||||
}
|
||||
|
||||
return result.data
|
||||
} catch (error) {
|
||||
console.error('[GraphQL] Query error:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет GraphQL мутацию
|
||||
* @param url - URL для запроса
|
||||
* @param mutation - GraphQL мутация
|
||||
* @param variables - Переменные мутации
|
||||
* @returns Результат мутации
|
||||
*/
|
||||
export function mutate<T = GraphQLData>(
|
||||
url: string,
|
||||
mutation: string,
|
||||
variables: Record<string, unknown> = {}
|
||||
): Promise<T> {
|
||||
return query<T>(url, mutation, variables)
|
||||
}
|
63
panel/graphql/mutations.ts
Normal file
63
panel/graphql/mutations.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
export const ADMIN_LOGIN_MUTATION = `
|
||||
mutation AdminLogin($email: String!, $password: String!) {
|
||||
login(email: $email, password: $password) {
|
||||
success
|
||||
token
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const ADMIN_LOGOUT_MUTATION = `
|
||||
mutation AdminLogout {
|
||||
logout {
|
||||
success
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const ADMIN_UPDATE_USER_MUTATION = `
|
||||
mutation AdminUpdateUser($user: AdminUserUpdateInput!) {
|
||||
adminUpdateUser(user: $user) {
|
||||
success
|
||||
error
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const ADMIN_UPDATE_ENV_VARIABLE_MUTATION = `
|
||||
mutation AdminUpdateEnvVariable($key: String!, $value: String!) {
|
||||
updateEnvVariable(key: $key, value: $value)
|
||||
}
|
||||
`
|
||||
|
||||
export const UPDATE_TOPIC_MUTATION = `
|
||||
mutation UpdateTopic($topic_input: TopicInput!) {
|
||||
update_topic(topic_input: $topic_input) {
|
||||
error
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const DELETE_TOPIC_MUTATION = `
|
||||
mutation DeleteTopic($id: Int!) {
|
||||
delete_topic_by_id(id: $id) {
|
||||
error
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const UPDATE_COMMUNITY_MUTATION = `
|
||||
mutation UpdateCommunity($community_input: CommunityInput!) {
|
||||
update_community(community_input: $community_input) {
|
||||
error
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const DELETE_COMMUNITY_MUTATION = `
|
||||
mutation DeleteCommunity($slug: String!) {
|
||||
delete_community(slug: $slug) {
|
||||
error
|
||||
}
|
||||
}
|
||||
`
|
156
panel/graphql/queries.ts
Normal file
156
panel/graphql/queries.ts
Normal file
@@ -0,0 +1,156 @@
|
||||
import { gql } from 'graphql-tag'
|
||||
|
||||
// Определяем GraphQL запрос
|
||||
export const ADMIN_GET_SHOUTS_QUERY: string =
|
||||
gql`
|
||||
query AdminGetShouts($limit: Int, $offset: Int, $search: String, $status: String) {
|
||||
adminGetShouts(limit: $limit, offset: $offset, search: $search, status: $status) {
|
||||
shouts {
|
||||
id
|
||||
title
|
||||
slug
|
||||
body
|
||||
lead
|
||||
subtitle
|
||||
layout
|
||||
lang
|
||||
cover
|
||||
cover_caption
|
||||
media {
|
||||
url
|
||||
title
|
||||
body
|
||||
source
|
||||
pic
|
||||
date
|
||||
genre
|
||||
artist
|
||||
lyrics
|
||||
}
|
||||
seo
|
||||
created_at
|
||||
updated_at
|
||||
published_at
|
||||
featured_at
|
||||
deleted_at
|
||||
created_by {
|
||||
id
|
||||
email
|
||||
name
|
||||
}
|
||||
authors {
|
||||
id
|
||||
name
|
||||
email
|
||||
}
|
||||
topics {
|
||||
id
|
||||
title
|
||||
slug
|
||||
}
|
||||
stat {
|
||||
rating
|
||||
comments_count
|
||||
viewed
|
||||
}
|
||||
}
|
||||
total
|
||||
page
|
||||
perPage
|
||||
totalPages
|
||||
}
|
||||
}
|
||||
`.loc?.source.body || ''
|
||||
|
||||
export const ADMIN_GET_USERS_QUERY: string =
|
||||
gql`
|
||||
query AdminGetUsers($limit: Int, $offset: Int, $search: String) {
|
||||
adminGetUsers(limit: $limit, offset: $offset, search: $search) {
|
||||
authors {
|
||||
id
|
||||
email
|
||||
name
|
||||
slug
|
||||
roles
|
||||
created_at
|
||||
last_seen
|
||||
}
|
||||
total
|
||||
page
|
||||
perPage
|
||||
totalPages
|
||||
}
|
||||
}
|
||||
`.loc?.source.body || ''
|
||||
|
||||
export const ADMIN_GET_ROLES_QUERY: string =
|
||||
gql`
|
||||
query AdminGetRoles {
|
||||
adminGetRoles {
|
||||
id
|
||||
name
|
||||
description
|
||||
}
|
||||
}
|
||||
`.loc?.source.body || ''
|
||||
|
||||
export const ADMIN_GET_ENV_VARIABLES_QUERY: string =
|
||||
gql`
|
||||
query GetEnvVariables {
|
||||
getEnvVariables {
|
||||
name
|
||||
description
|
||||
variables {
|
||||
key
|
||||
value
|
||||
description
|
||||
type
|
||||
isSecret
|
||||
}
|
||||
}
|
||||
}
|
||||
`.loc?.source.body || ''
|
||||
|
||||
export const GET_COMMUNITIES_QUERY: string =
|
||||
gql`
|
||||
query GetCommunities {
|
||||
get_communities_all {
|
||||
id
|
||||
slug
|
||||
name
|
||||
desc
|
||||
pic
|
||||
created_at
|
||||
created_by {
|
||||
id
|
||||
name
|
||||
email
|
||||
}
|
||||
stat {
|
||||
shouts
|
||||
followers
|
||||
authors
|
||||
}
|
||||
}
|
||||
}
|
||||
`.loc?.source.body || ''
|
||||
|
||||
export const GET_TOPICS_QUERY: string =
|
||||
gql`
|
||||
query GetTopics {
|
||||
get_topics_all {
|
||||
id
|
||||
slug
|
||||
title
|
||||
body
|
||||
pic
|
||||
community
|
||||
parent_ids
|
||||
stat {
|
||||
shouts
|
||||
authors
|
||||
followers
|
||||
}
|
||||
}
|
||||
}
|
||||
`.loc?.source.body || ''
|
Reference in New Issue
Block a user