Implement support ticket system with role request and bug report functionalities
CI / Backend (build + test) (push) Successful in 1m18s
CI / Frontend (lint + typecheck + build) (push) Successful in 31s

- Introduced a new support ticket system allowing users to submit bug reports and role requests.
- Implemented endpoints for creating, updating, and managing support tickets, including file attachments.
- Enhanced Telegram bot integration to handle role requests directly within the bot, enabling admins to approve or reject requests without accessing the website.
- Updated database schema to include support ticket entities and their relationships.
- Improved API documentation to reflect new support ticket endpoints and their usage.
- Added necessary localization for support ticket features in both Russian and English.
This commit is contained in:
Leonid Pershin
2026-07-14 06:49:05 +03:00
parent 14b64a3140
commit b5630b2685
98 changed files with 4463 additions and 6 deletions
+33
View File
@@ -91,3 +91,36 @@ export async function apiRequest<T>(path: string, options: RequestOptions = {}):
const text = await response.text()
return (text ? JSON.parse(text) : undefined) as T
}
type UploadOptions = {
method?: 'POST' | 'PUT'
skipRefresh?: boolean
}
/** Как apiRequest, но для multipart/form-data (вложения к тикетам) — без JSON.stringify и
* без Content-Type (браузер сам проставляет boundary). */
export async function apiUpload<T>(path: string, formData: FormData, options: UploadOptions = {}): Promise<T> {
const headers: Record<string, string> = {}
if (accessToken) headers.Authorization = `Bearer ${accessToken}`
const response = await fetch(`/api${path}`, {
method: options.method ?? 'POST',
headers,
credentials: 'include',
body: formData,
})
if (response.status === 401 && !options.skipRefresh) {
const refreshed = await refreshAccessToken()
if (refreshed) return apiUpload<T>(path, formData, { ...options, skipRefresh: true })
onUnauthorized?.()
throw await parseError(response)
}
if (!response.ok) throw await parseError(response)
if (response.status === 204) return undefined as T
const text = await response.text()
return (text ? JSON.parse(text) : undefined) as T
}