frontAnswerer/lib/api/quizRelase.ts
Nastya dab5b69821
All checks were successful
Deploy / CreateImage (push) Successful in 3m5s
Deploy / DeployService (push) Successful in 21s
hide ai
2025-04-28 03:05:02 +03:00

339 lines
8.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { GetQuizDataResponse, parseQuizData } from "@model/api/getQuizData";
import axios from "axios";
import MobileDetect from "mobile-detect";
import device from "current-device";
import type { AxiosError } from "axios";
import { replaceSpacesToEmptyLines } from "../components/ViewPublicationPage/tools/replaceSpacesToEmptyLines";
import { QuizSettings } from "@model/settingsData";
import * as Bowser from "bowser";
import { domain } from "../utils/defineDomain";
let SESSIONS = "";
const md = new MobileDetect(window.navigator.userAgent);
const userAgent = navigator.userAgent;
//операционная система
let OSDevice: string | undefined;
if (userAgent.toLowerCase().includes("linux")) {
OSDevice = "Linux";
}
if (userAgent.toLowerCase().includes("windows")) {
OSDevice = "Windows";
}
if (/iPad|iPhone|iPod/.test(userAgent)) {
OSDevice = "IOS";
}
if (userAgent.toLowerCase().includes("macintosh")) {
OSDevice = "Mac OS";
}
if (OSDevice === undefined) {
OSDevice = userAgent;
}
//браузер
let browserUser: string;
if (Bowser.name === "Chrome") {
browserUser = "Chrome";
} else if (Bowser.name === "Firefox") {
browserUser = "Firefox";
} else if (Bowser.name === "Safari") {
browserUser = "Safari";
} else if (Bowser.name === "Yandex Browser") {
browserUser = "Yandex Browser";
} else {
browserUser = userAgent;
}
const DeviceType = device.type;
let Device = md.mobile();
if (Device === null) {
Device = userAgent;
}
type PublicationMakeRequestParams = {
url: string;
body: FormData;
method: "POST";
};
export const publicationMakeRequest = ({ url, body }: PublicationMakeRequestParams) => {
return axios(url, {
data: body,
headers: {
"X-Sessionkey": SESSIONS,
"Content-Type": "multipart/form-data",
DeviceType: DeviceType,
Device: Device,
OS: OSDevice,
Browser: browserUser,
},
method: "POST",
});
};
let page = 0;
export async function getData(quizId: string): Promise<{
data: GetQuizDataResponse | null;
isRecentlyCompleted: boolean;
error?: AxiosError;
}> {
try {
const { data, headers } = await axios<GetQuizDataResponse>(
domain + `/answer/v1.0.0/settings${window.location.search}`,
{
method: "POST",
headers: {
"X-Sessionkey": SESSIONS,
"Content-Type": "application/json",
DeviceType: DeviceType,
Device: Device,
OS: OSDevice,
Browser: userAgent,
},
data: {
quiz_id: quizId,
limit: 1,
page,
need_config: page === 0,
},
}
);
console.log(data);
if (data.items.length) page++;
const sessions = JSON.parse(localStorage.getItem("sessions") || "{}");
//Тут ещё проверка на антифрод без парса конфига. Нам не интересно время если не нужно запрещать проходить чаще чем в сутки
if (!page && typeof sessions[quizId] === "number" && data.settings.cfg.includes('antifraud":true')) {
// unix время. Если меньше суток прошло - выводить ошибку, иначе пустить дальше
if (Date.now() - sessions[quizId] < 86400000) {
return { data, isRecentlyCompleted: true };
}
}
SESSIONS = headers["x-sessionkey"] ? headers["x-sessionkey"] : SESSIONS;
return { data, isRecentlyCompleted: false };
} catch (nativeError) {
const error = nativeError as AxiosError;
return { data: null, isRecentlyCompleted: false, error: error };
}
}
export async function getQuizData(quizId: string, maxRetries = 3): Promise<QuizSettings> {
if (!quizId) throw new Error("No quiz id");
let retryCount = 0;
let lastError: Error | null = null;
while (retryCount < maxRetries) {
try {
const response = await getData(quizId);
const quizDataResponse = response.data;
console.log("ф-я аналитики");
console.log(response);
if (response.error) {
throw response.error;
}
if (!quizDataResponse) {
throw new Error("Quiz not found");
}
// Проверка на AI result
const hasAiResult = quizDataResponse.items.some(
(item) => item.typ === "result" && quizDataResponse.settings.status === "ai"
);
if (hasAiResult && retryCount < maxRetries - 1) {
retryCount++;
continue; // Повторяем запрос
} else {
retryCount = maxRetries;
}
let isEmpty = !response.data?.items.length;
while (isEmpty) {
await new Promise((resolve) =>
setTimeout(async () => {
const response = await getData(quizId);
isEmpty = !response.data?.items.length;
}, 1000)
);
}
const quizSettings = replaceSpacesToEmptyLines(parseQuizData(quizDataResponse));
const res = JSON.parse(
JSON.stringify({ data: quizSettings })
.replaceAll(/\\" \\"/g, '""')
.replaceAll(/" "/g, '""')
).data as QuizSettings;
res.recentlyCompleted = response.isRecentlyCompleted;
return res;
} catch (error) {
lastError = error as Error;
retryCount++;
if (retryCount >= maxRetries) {
break;
}
// Добавляем небольшую задержку перед повторным запросом
await new Promise((resolve) => setTimeout(resolve, 1000 * retryCount));
}
}
throw lastError || new Error("Failed to get quiz data after retries");
}
type SendAnswerProps = {
questionId: string;
body: string | string[];
qid: string;
preview?: boolean;
};
export function sendAnswer({ questionId, body, qid, preview = false }: SendAnswerProps) {
if (preview) return;
const formData = new FormData();
const answers = [
{
question_id: questionId,
content: body, //тут массив с ответом
},
];
formData.append("answers", JSON.stringify(answers));
formData.append("qid", qid);
return publicationMakeRequest({
url: domain + `/answer/v1.0.0/answer`,
body: formData,
method: "POST",
});
}
//body ={file, filename}
type SendFileParams = {
questionId: string;
body: {
name: string;
file: File;
preview: boolean;
};
qid: string;
};
type Answer = {
question_id: string;
content: string;
};
export function sendFile({ questionId, body, qid }: SendFileParams) {
if (body.preview) return;
const formData = new FormData();
const file = new File([body.file], body.file.name.replace(/\s/g, "_"));
const nameImage = body.name.replace(/\s/g, "_");
const answers: Answer[] = [
{
question_id: questionId,
content: "file:" + nameImage,
},
];
formData.append("answers", JSON.stringify(answers));
formData.append(nameImage, file);
formData.append("qid", qid);
return publicationMakeRequest({
url: domain + `/answer/v1.0.0/answer`,
body: formData,
method: "POST",
});
}
//форма контактов
export type SendFCParams = {
questionId: string;
body: {
name?: string;
email?: string;
phone?: string;
address?: string;
customs?: Record<string, string>;
};
qid: string;
preview: boolean;
};
export function sendFC({ questionId, body, qid, preview }: SendFCParams) {
if (preview) return;
const formData = new FormData();
// const keysBody = Object.keys(body)
// const content:any = {}
// fields.forEach((key) => {
// if (keysBody.includes(key)) content[key] = body.key
// })
const answers = [
{
question_id: questionId,
content: JSON.stringify(body),
result: true,
qid,
},
];
formData.append("answers", JSON.stringify(answers));
formData.append("qid", qid);
return publicationMakeRequest({
url: domain + `/answer/v1.0.0/answer`,
body: formData,
method: "POST",
});
}
//форма контактов
export type SendResultParams = {
questionId: string;
pointsSum: number;
qid: string;
preview: boolean;
};
export function sendResult({ questionId, pointsSum, qid, preview }: SendResultParams) {
if (preview) return;
const formData = new FormData();
// const keysBody = Object.keys(body)
// const content:any = {}
// fields.forEach((key) => {
// if (keysBody.includes(key)) content[key] = body.key
// })
const answers = [
{
question_id: questionId,
content: pointsSum.toString(),
result: false,
qid,
},
];
formData.append("answers", JSON.stringify(answers));
formData.append("qid", qid);
return publicationMakeRequest({
url: domain + `/answer/v1.0.0/answer`,
body: formData,
method: "POST",
});
}