add upload logs
This commit is contained in:
parent
214ac28926
commit
9449a45ef9
|
|
@ -42,18 +42,38 @@ export const PresubmitStep = ({ prevStep }: PresubmitStepProps) => {
|
||||||
setIsErrorUploadModal(false);
|
setIsErrorUploadModal(false);
|
||||||
uploadFile.resetUploadError();
|
uploadFile.resetUploadError();
|
||||||
uploadCover.resetUploadError();
|
uploadCover.resetUploadError();
|
||||||
|
console.info("[App2Client][Presubmit] Пользователь закрыл модалку ошибок загрузки");
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (uploadFile.uploadError || uploadCover.uploadError) {
|
if (uploadFile.uploadError || uploadCover.uploadError) {
|
||||||
|
console.error("[App2Client][Presubmit] Обнаружены ошибки загрузки", {
|
||||||
|
fileError: uploadFile.uploadError,
|
||||||
|
coverError: uploadCover.uploadError,
|
||||||
|
});
|
||||||
setIsErrorUploadModal(true);
|
setIsErrorUploadModal(true);
|
||||||
}
|
}
|
||||||
}, [uploadFile.uploadError, uploadCover.uploadError]);
|
}, [uploadFile.uploadError, uploadCover.uploadError]);
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
try {
|
try {
|
||||||
|
console.info("[App2Client][Presubmit] Начинаем отправку контента", {
|
||||||
|
name: rootStore.name,
|
||||||
|
author: rootStore.author,
|
||||||
|
fileName: rootStore.file?.name,
|
||||||
|
fileSize: rootStore.file?.size,
|
||||||
|
fileType: rootStore.file?.type,
|
||||||
|
allowCover: rootStore.allowCover,
|
||||||
|
hasCover: Boolean(rootStore.cover),
|
||||||
|
allowDownload: rootStore.allowDwnld,
|
||||||
|
hashtags: rootStore.hashtags,
|
||||||
|
royaltyCount: rootStore.royalty.length,
|
||||||
|
allowResale: rootStore.allowResale,
|
||||||
|
});
|
||||||
|
|
||||||
let coverUploadResult = { content_id_v1: "" };
|
let coverUploadResult = { content_id_v1: "" };
|
||||||
|
|
||||||
|
console.info("[App2Client][Presubmit] Загружаем основной файл через tus");
|
||||||
const fileUploadResult = await uploadFile.mutateAsync({
|
const fileUploadResult = await uploadFile.mutateAsync({
|
||||||
file: rootStore.file as File,
|
file: rootStore.file as File,
|
||||||
metadata: {
|
metadata: {
|
||||||
|
|
@ -70,12 +90,37 @@ export const PresubmitStep = ({ prevStep }: PresubmitStepProps) => {
|
||||||
throw new Error("Tus upload did not return encrypted cid");
|
throw new Error("Tus upload did not return encrypted cid");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.info("[App2Client][Presubmit] Основной файл загружен", {
|
||||||
|
encryptedCid: fileUploadResult.encryptedCid,
|
||||||
|
uploadId: fileUploadResult.uploadId,
|
||||||
|
state: fileUploadResult.state,
|
||||||
|
sizeBytes: fileUploadResult.sizeBytes,
|
||||||
|
});
|
||||||
|
|
||||||
if (rootStore.allowCover && rootStore.cover) {
|
if (rootStore.allowCover && rootStore.cover) {
|
||||||
|
console.info("[App2Client][Presubmit] Начинаем загрузку обложки", {
|
||||||
|
fileName: rootStore.cover.name,
|
||||||
|
fileSize: rootStore.cover.size,
|
||||||
|
fileType: rootStore.cover.type,
|
||||||
|
});
|
||||||
coverUploadResult = await uploadCover.mutateAsync(
|
coverUploadResult = await uploadCover.mutateAsync(
|
||||||
rootStore.cover as File,
|
rootStore.cover as File,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
console.info("[App2Client][Presubmit] Обложка загружена", {
|
||||||
|
contentIdV1: coverUploadResult.content_id_v1,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.info("[App2Client][Presubmit] Обложка не загружается", {
|
||||||
|
allowCover: rootStore.allowCover,
|
||||||
|
hasCoverFile: Boolean(rootStore.cover),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.info("[App2Client][Presubmit] Отправляем метаданные контента", {
|
||||||
|
downloadable: rootStore.allowDwnld,
|
||||||
|
coverIncluded: Boolean(coverUploadResult.content_id_v1),
|
||||||
|
});
|
||||||
const createContentResponse = await createContent.mutateAsync({
|
const createContentResponse = await createContent.mutateAsync({
|
||||||
title: rootStore.name,
|
title: rootStore.name,
|
||||||
|
|
||||||
|
|
@ -112,6 +157,10 @@ export const PresubmitStep = ({ prevStep }: PresubmitStepProps) => {
|
||||||
|
|
||||||
if (createContentResponse.data) {
|
if (createContentResponse.data) {
|
||||||
if (createContentResponse.data.address != "free") {
|
if (createContentResponse.data.address != "free") {
|
||||||
|
console.info("[App2Client][Presubmit] Отправляем транзакцию через TonConnect", {
|
||||||
|
address: createContentResponse.data.address,
|
||||||
|
amount: createContentResponse.data.amount,
|
||||||
|
});
|
||||||
const transactionResponse = await tonConnectUI.sendTransaction({
|
const transactionResponse = await tonConnectUI.sendTransaction({
|
||||||
validUntil: Math.floor(Date.now() / 1000) + 120,
|
validUntil: Math.floor(Date.now() / 1000) + 120,
|
||||||
messages: [
|
messages: [
|
||||||
|
|
@ -123,19 +172,29 @@ export const PresubmitStep = ({ prevStep }: PresubmitStepProps) => {
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
if (transactionResponse.boc) {
|
if (transactionResponse.boc) {
|
||||||
|
console.info("[App2Client][Presubmit] Транзакция успешно отправлена", {
|
||||||
|
bocLength: transactionResponse.boc.length,
|
||||||
|
});
|
||||||
WebApp.close();
|
WebApp.close();
|
||||||
} else {
|
} else {
|
||||||
console.error("Transaction failed:", transactionResponse);
|
console.error("Transaction failed:", transactionResponse);
|
||||||
|
console.error("[App2Client][Presubmit] Транзакция не отправлена", {
|
||||||
|
response: transactionResponse,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.info("[App2Client][Presubmit] Завершаем процесс и закрываем WebApp");
|
||||||
WebApp.close();
|
WebApp.close();
|
||||||
// @ts-expect-error Type issues
|
// @ts-expect-error Type issues
|
||||||
} catch (error: never) {
|
} catch (error: never) {
|
||||||
|
|
||||||
|
|
||||||
console.error("An error occurred during the submission process:", error);
|
console.error("An error occurred during the submission process:", error);
|
||||||
|
console.error("[App2Client][Presubmit] Ошибка во время отправки", {
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
|
||||||
if (error?.status === 400) {
|
if (error?.status === 400) {
|
||||||
alert(
|
alert(
|
||||||
|
|
|
||||||
|
|
@ -20,16 +20,44 @@ type UseCreateNewContentPayload = {
|
||||||
export const useCreateNewContent = () => {
|
export const useCreateNewContent = () => {
|
||||||
return useMutation(
|
return useMutation(
|
||||||
["create-new-content"],
|
["create-new-content"],
|
||||||
(payload: UseCreateNewContentPayload) => {
|
async (payload: UseCreateNewContentPayload) => {
|
||||||
return request.post<{
|
console.info("[App2Client][Content] Отправляем контент на создание", {
|
||||||
address: string;
|
title: payload.title,
|
||||||
amount: string;
|
hasImage: Boolean(payload.image),
|
||||||
payload: string;
|
contentCid: payload.content,
|
||||||
}>("/blockchain.sendNewContentMessage", payload, {
|
hashtags: payload.hashtags,
|
||||||
headers: {
|
authorsCount: payload.authors.length,
|
||||||
Authorization: localStorage.getItem('auth_v1_token') ?? ""
|
royaltyCount: payload.royaltyParams.length,
|
||||||
}
|
downloadable: payload.downloadable,
|
||||||
|
allowResale: payload.allowResale,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await request.post<{
|
||||||
|
address: string;
|
||||||
|
amount: string;
|
||||||
|
payload: string;
|
||||||
|
}>("/blockchain.sendNewContentMessage", payload, {
|
||||||
|
headers: {
|
||||||
|
Authorization: localStorage.getItem('auth_v1_token') ?? ""
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.info("[App2Client][Content] Сервер вернул данные для транзакции", {
|
||||||
|
title: payload.title,
|
||||||
|
hasAddress: Boolean(response.data.address),
|
||||||
|
amount: response.data.amount,
|
||||||
|
payloadLength: response.data.payload?.length ?? 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[App2Client][Content] Ошибка при создании контента", {
|
||||||
|
title: payload.title,
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -94,8 +94,16 @@ const pollUploadStatus = async (
|
||||||
signal?: AbortSignal,
|
signal?: AbortSignal,
|
||||||
): Promise<UploadStatusResponse> => {
|
): Promise<UploadStatusResponse> => {
|
||||||
const startedAt = Date.now();
|
const startedAt = Date.now();
|
||||||
|
let attempt = 0;
|
||||||
|
|
||||||
|
console.info("[App2Client][TusUpload] Запускаем polling статуса", {
|
||||||
|
uploadId,
|
||||||
|
intervalMs: TUS_STATUS_POLL_INTERVAL_MS,
|
||||||
|
timeoutMs: TUS_STATUS_POLL_TIMEOUT_MS,
|
||||||
|
});
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
attempt += 1;
|
||||||
if (signal?.aborted) {
|
if (signal?.aborted) {
|
||||||
throw new DOMException("Tus status polling aborted", "AbortError");
|
throw new DOMException("Tus status polling aborted", "AbortError");
|
||||||
}
|
}
|
||||||
|
|
@ -108,25 +116,58 @@ const pollUploadStatus = async (
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
console.info("[App2Client][TusUpload] Получен статус tus", {
|
||||||
|
uploadId,
|
||||||
|
attempt,
|
||||||
|
state: data.state,
|
||||||
|
hasEncryptedCid: Boolean(data.encrypted_cid),
|
||||||
|
});
|
||||||
|
|
||||||
if (data.state === "failed") {
|
if (data.state === "failed") {
|
||||||
throw new Error(data.error || "Tus upload failed on server");
|
throw new Error(data.error || "Tus upload failed on server");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.state === "pinned" && data.encrypted_cid) {
|
if (data.state === "pinned" && data.encrypted_cid) {
|
||||||
|
console.info("[App2Client][TusUpload] Статус tus завершен", {
|
||||||
|
uploadId,
|
||||||
|
state: data.state,
|
||||||
|
encryptedCid: data.encrypted_cid,
|
||||||
|
sizeBytes: data.size_bytes,
|
||||||
|
attempts: attempt,
|
||||||
|
durationMs: Date.now() - startedAt,
|
||||||
|
});
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const maybeAxios = error as { response?: { status?: number } };
|
const maybeAxios = error as { response?: { status?: number } };
|
||||||
if (maybeAxios?.response?.status === 404) {
|
if (maybeAxios?.response?.status === 404) {
|
||||||
// Hook has not recorded the upload session yet; continue polling.
|
// Hook has not recorded the upload session yet; continue polling.
|
||||||
|
console.info("[App2Client][TusUpload] Статус tus еще не готов (404)", {
|
||||||
|
uploadId,
|
||||||
|
attempt,
|
||||||
|
});
|
||||||
} else if (error instanceof DOMException && error.name === "AbortError") {
|
} else if (error instanceof DOMException && error.name === "AbortError") {
|
||||||
|
console.warn("[App2Client][TusUpload] Поллинг tus прерван", {
|
||||||
|
uploadId,
|
||||||
|
attempt,
|
||||||
|
});
|
||||||
throw error;
|
throw error;
|
||||||
} else {
|
} else {
|
||||||
|
console.error("[App2Client][TusUpload] Ошибка во время polling", {
|
||||||
|
uploadId,
|
||||||
|
attempt,
|
||||||
|
error,
|
||||||
|
});
|
||||||
throw normalizeError(error, "Tus status polling failed");
|
throw normalizeError(error, "Tus status polling failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Date.now() - startedAt > TUS_STATUS_POLL_TIMEOUT_MS) {
|
if (Date.now() - startedAt > TUS_STATUS_POLL_TIMEOUT_MS) {
|
||||||
|
console.error("[App2Client][TusUpload] Поллинг tus превысил таймаут", {
|
||||||
|
uploadId,
|
||||||
|
attempts: attempt,
|
||||||
|
durationMs: Date.now() - startedAt,
|
||||||
|
});
|
||||||
throw new Error("Timed out waiting for tus upload finalization");
|
throw new Error("Timed out waiting for tus upload finalization");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -153,6 +194,7 @@ export const useTusUpload = () => {
|
||||||
const [isUploading, setIsUploading] = useState(false);
|
const [isUploading, setIsUploading] = useState(false);
|
||||||
const [uploadError, setUploadError] = useState<Error | null>(null);
|
const [uploadError, setUploadError] = useState<Error | null>(null);
|
||||||
const activeUploadRef = useRef<Upload | null>(null);
|
const activeUploadRef = useRef<Upload | null>(null);
|
||||||
|
const lastLoggedProgressRef = useRef(-10);
|
||||||
|
|
||||||
const mutation = useMutation<TusUploadResult, Error, TusUploadArgs>(
|
const mutation = useMutation<TusUploadResult, Error, TusUploadArgs>(
|
||||||
["upload-file", "tus"],
|
["upload-file", "tus"],
|
||||||
|
|
@ -161,15 +203,16 @@ export const useTusUpload = () => {
|
||||||
throw new Error("File is required for tus upload");
|
throw new Error("File is required for tus upload");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TUS_ENDPOINT) {
|
|
||||||
throw new Error("Tus endpoint is not configured");
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsUploading(true);
|
setIsUploading(true);
|
||||||
setUploadProgress(0);
|
setUploadProgress(0);
|
||||||
setUploadError(null);
|
setUploadError(null);
|
||||||
|
let lastError: Error | null = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (!TUS_ENDPOINT) {
|
||||||
|
throw new Error("Tus endpoint is not configured");
|
||||||
|
}
|
||||||
|
|
||||||
const result = await new Promise<TusUploadResult>((resolve, reject) => {
|
const result = await new Promise<TusUploadResult>((resolve, reject) => {
|
||||||
const token = localStorage.getItem("auth_v1_token") ?? "";
|
const token = localStorage.getItem("auth_v1_token") ?? "";
|
||||||
const normalizedMeta = normalizeMetadata(metadata);
|
const normalizedMeta = normalizeMetadata(metadata);
|
||||||
|
|
@ -178,6 +221,13 @@ export const useTusUpload = () => {
|
||||||
headers.Authorization = token;
|
headers.Authorization = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.info("[App2Client][TusUpload] Начинаем загрузку", {
|
||||||
|
fileName: file.name,
|
||||||
|
fileSize: file.size,
|
||||||
|
fileType: file.type,
|
||||||
|
metadata: normalizedMeta,
|
||||||
|
});
|
||||||
|
|
||||||
const upload = new Upload(file, {
|
const upload = new Upload(file, {
|
||||||
endpoint: TUS_ENDPOINT,
|
endpoint: TUS_ENDPOINT,
|
||||||
metadata: {
|
metadata: {
|
||||||
|
|
@ -191,6 +241,7 @@ export const useTusUpload = () => {
|
||||||
uploadDataDuringCreation: true,
|
uploadDataDuringCreation: true,
|
||||||
onError: (err) => {
|
onError: (err) => {
|
||||||
const normalized = normalizeError(err, "Tus upload failed");
|
const normalized = normalizeError(err, "Tus upload failed");
|
||||||
|
lastError = normalized;
|
||||||
setUploadError(normalized);
|
setUploadError(normalized);
|
||||||
setIsUploading(false);
|
setIsUploading(false);
|
||||||
setUploadProgress(0);
|
setUploadProgress(0);
|
||||||
|
|
@ -202,6 +253,16 @@ export const useTusUpload = () => {
|
||||||
? Math.floor((bytesUploaded / bytesTotal) * 100)
|
? Math.floor((bytesUploaded / bytesTotal) * 100)
|
||||||
: 0;
|
: 0;
|
||||||
setUploadProgress(Math.min(99, Math.max(0, percent)));
|
setUploadProgress(Math.min(99, Math.max(0, percent)));
|
||||||
|
|
||||||
|
if (percent >= lastLoggedProgressRef.current + 5 || percent === 100) {
|
||||||
|
lastLoggedProgressRef.current = percent;
|
||||||
|
console.info("[App2Client][TusUpload] Прогресс загрузки", {
|
||||||
|
fileName: file.name,
|
||||||
|
bytesUploaded,
|
||||||
|
bytesTotal,
|
||||||
|
percent,
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
(async () => {
|
(async () => {
|
||||||
|
|
@ -213,6 +274,10 @@ export const useTusUpload = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const uploadId = extractUploadId(uploadUrl);
|
const uploadId = extractUploadId(uploadUrl);
|
||||||
|
console.info("[App2Client][TusUpload] Загрузка завершена на tus, начинаем финализацию", {
|
||||||
|
fileName: file.name,
|
||||||
|
uploadId,
|
||||||
|
});
|
||||||
const status = await pollUploadStatus(uploadId, signal);
|
const status = await pollUploadStatus(uploadId, signal);
|
||||||
const encryptedCid = status.encrypted_cid;
|
const encryptedCid = status.encrypted_cid;
|
||||||
if (!encryptedCid) {
|
if (!encryptedCid) {
|
||||||
|
|
@ -223,6 +288,14 @@ export const useTusUpload = () => {
|
||||||
setIsUploading(false);
|
setIsUploading(false);
|
||||||
activeUploadRef.current = null;
|
activeUploadRef.current = null;
|
||||||
|
|
||||||
|
console.info("[App2Client][TusUpload] Финализация tus завершена", {
|
||||||
|
fileName: file.name,
|
||||||
|
uploadId,
|
||||||
|
encryptedCid,
|
||||||
|
finalState: status.state,
|
||||||
|
sizeBytes: status.size_bytes,
|
||||||
|
});
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
kind: "tus",
|
kind: "tus",
|
||||||
uploadId,
|
uploadId,
|
||||||
|
|
@ -235,6 +308,11 @@ export const useTusUpload = () => {
|
||||||
statusError,
|
statusError,
|
||||||
"Failed to finalize tus upload",
|
"Failed to finalize tus upload",
|
||||||
);
|
);
|
||||||
|
lastError = normalized;
|
||||||
|
console.error("[App2Client][TusUpload] Ошибка финализации tus", {
|
||||||
|
fileName: file.name,
|
||||||
|
error: normalized,
|
||||||
|
});
|
||||||
setUploadError(normalized);
|
setUploadError(normalized);
|
||||||
setIsUploading(false);
|
setIsUploading(false);
|
||||||
activeUploadRef.current = null;
|
activeUploadRef.current = null;
|
||||||
|
|
@ -245,6 +323,11 @@ export const useTusUpload = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
activeUploadRef.current = upload;
|
activeUploadRef.current = upload;
|
||||||
|
lastLoggedProgressRef.current = 0;
|
||||||
|
|
||||||
|
console.info("[App2Client][TusUpload] Запрашиваем предыдущие загрузки для возобновления", {
|
||||||
|
fileName: file.name,
|
||||||
|
});
|
||||||
|
|
||||||
if (signal) {
|
if (signal) {
|
||||||
signal.addEventListener(
|
signal.addEventListener(
|
||||||
|
|
@ -255,6 +338,10 @@ export const useTusUpload = () => {
|
||||||
"Tus upload aborted",
|
"Tus upload aborted",
|
||||||
"AbortError",
|
"AbortError",
|
||||||
);
|
);
|
||||||
|
console.warn("[App2Client][TusUpload] Загрузка прервана внешним сигналом", {
|
||||||
|
fileName: file.name,
|
||||||
|
});
|
||||||
|
lastError = abortError;
|
||||||
setUploadError(abortError);
|
setUploadError(abortError);
|
||||||
setIsUploading(false);
|
setIsUploading(false);
|
||||||
activeUploadRef.current = null;
|
activeUploadRef.current = null;
|
||||||
|
|
@ -267,16 +354,32 @@ export const useTusUpload = () => {
|
||||||
upload
|
upload
|
||||||
.findPreviousUploads()
|
.findPreviousUploads()
|
||||||
.then((previousUploads) => {
|
.then((previousUploads) => {
|
||||||
|
console.info("[App2Client][TusUpload] Результат поиска предыдущих загрузок", {
|
||||||
|
fileName: file.name,
|
||||||
|
count: previousUploads.length,
|
||||||
|
});
|
||||||
if (previousUploads.length > 0) {
|
if (previousUploads.length > 0) {
|
||||||
upload.resumeFromPreviousUpload(previousUploads[0]);
|
upload.resumeFromPreviousUpload(previousUploads[0]);
|
||||||
|
console.info("[App2Client][TusUpload] Возобновляем загрузку", {
|
||||||
|
fileName: file.name,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
upload.start();
|
upload.start();
|
||||||
|
console.info("[App2Client][TusUpload] Стартуем загрузку", {
|
||||||
|
fileName: file.name,
|
||||||
|
chunkSize: upload.options.chunkSize,
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.catch((resumeError) => {
|
.catch((resumeError) => {
|
||||||
const normalized = normalizeError(
|
const normalized = normalizeError(
|
||||||
resumeError,
|
resumeError,
|
||||||
"Failed to resume tus upload",
|
"Failed to resume tus upload",
|
||||||
);
|
);
|
||||||
|
lastError = normalized;
|
||||||
|
console.error("[App2Client][TusUpload] Не удалось возобновить загрузку", {
|
||||||
|
fileName: file.name,
|
||||||
|
error: normalized,
|
||||||
|
});
|
||||||
setUploadError(normalized);
|
setUploadError(normalized);
|
||||||
setIsUploading(false);
|
setIsUploading(false);
|
||||||
activeUploadRef.current = null;
|
activeUploadRef.current = null;
|
||||||
|
|
@ -285,9 +388,25 @@ export const useTusUpload = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
const normalized = normalizeError(error, "Tus upload failed");
|
||||||
|
if (!lastError) {
|
||||||
|
lastError = normalized;
|
||||||
|
console.error("[App2Client][TusUpload] Ошибка во время загрузки", {
|
||||||
|
fileName: file.name,
|
||||||
|
error: normalized,
|
||||||
|
});
|
||||||
|
setUploadError(normalized);
|
||||||
|
}
|
||||||
|
setIsUploading(false);
|
||||||
|
throw normalized;
|
||||||
} finally {
|
} finally {
|
||||||
activeUploadRef.current = null;
|
activeUploadRef.current = null;
|
||||||
setIsUploading(false);
|
setIsUploading(false);
|
||||||
|
console.info("[App2Client][TusUpload] Завершили работу загрузчика", {
|
||||||
|
fileName: file?.name,
|
||||||
|
hasError: Boolean(lastError),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -300,6 +419,9 @@ export const useTusUpload = () => {
|
||||||
activeUploadRef.current = null;
|
activeUploadRef.current = null;
|
||||||
setIsUploading(false);
|
setIsUploading(false);
|
||||||
setUploadProgress(0);
|
setUploadProgress(0);
|
||||||
|
console.warn("[App2Client][TusUpload] Принудительно остановили загрузку", {
|
||||||
|
reason: "manual",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -317,11 +439,21 @@ export const useLegacyUploadFile = () => {
|
||||||
const [uploadProgress, setUploadProgress] = useState(0);
|
const [uploadProgress, setUploadProgress] = useState(0);
|
||||||
const [isUploading, setIsUploading] = useState(false);
|
const [isUploading, setIsUploading] = useState(false);
|
||||||
const [uploadError, setUploadError] = useState<Error | null>(null);
|
const [uploadError, setUploadError] = useState<Error | null>(null);
|
||||||
|
const lastLoggedProgressRef = useRef(-10);
|
||||||
|
|
||||||
const mutation = useMutation(["upload-file", "legacy"], async (file: File) => {
|
const mutation = useMutation(["upload-file", "legacy"], async (file: File) => {
|
||||||
setIsUploading(true);
|
setIsUploading(true);
|
||||||
setUploadProgress(0);
|
setUploadProgress(0);
|
||||||
setUploadError(null);
|
setUploadError(null);
|
||||||
|
lastLoggedProgressRef.current = 0;
|
||||||
|
let lastError: Error | null = null;
|
||||||
|
|
||||||
|
console.info("[App2Client][LegacyUpload] Начинаем загрузку", {
|
||||||
|
fileName: file.name,
|
||||||
|
fileSize: file.size,
|
||||||
|
fileType: file.type,
|
||||||
|
isChunked: file.size > MAX_CHUNK_SIZE,
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (file.size <= MAX_CHUNK_SIZE) {
|
if (file.size <= MAX_CHUNK_SIZE) {
|
||||||
|
|
@ -352,11 +484,35 @@ export const useLegacyUploadFile = () => {
|
||||||
(progressEvent.loaded * 100) / total,
|
(progressEvent.loaded * 100) / total,
|
||||||
);
|
);
|
||||||
setUploadProgress(Math.min(99, percentCompleted));
|
setUploadProgress(Math.min(99, percentCompleted));
|
||||||
|
|
||||||
|
if (
|
||||||
|
percentCompleted >= lastLoggedProgressRef.current + 5 ||
|
||||||
|
percentCompleted === 100
|
||||||
|
) {
|
||||||
|
lastLoggedProgressRef.current = percentCompleted;
|
||||||
|
console.info("[App2Client][LegacyUpload] Прогресс загрузки", {
|
||||||
|
fileName: file.name,
|
||||||
|
percent: percentCompleted,
|
||||||
|
loaded: progressEvent.loaded,
|
||||||
|
total,
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
setUploadProgress(100);
|
setUploadProgress(100);
|
||||||
|
|
||||||
|
console.info("[App2Client][LegacyUpload] Загрузка завершена", {
|
||||||
|
fileName: file.name,
|
||||||
|
response: {
|
||||||
|
hasContentId: Boolean(
|
||||||
|
response.data.content_id_v1 || response.data.content_id,
|
||||||
|
),
|
||||||
|
hasSha256: Boolean(response.data.content_sha256),
|
||||||
|
uploadId: response.data.upload_id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content_sha256: response.data.content_sha256 || "",
|
content_sha256: response.data.content_sha256 || "",
|
||||||
content_id_v1:
|
content_id_v1:
|
||||||
|
|
@ -392,6 +548,15 @@ export const useLegacyUploadFile = () => {
|
||||||
headers["X-Upload-ID"] = uploadId;
|
headers["X-Upload-ID"] = uploadId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.info("[App2Client][LegacyUpload] Отправляем chunk", {
|
||||||
|
fileName: file.name,
|
||||||
|
chunkStart: offset,
|
||||||
|
chunkEnd,
|
||||||
|
chunkSize: chunk.size,
|
||||||
|
isLastChunk,
|
||||||
|
uploadId,
|
||||||
|
});
|
||||||
|
|
||||||
const response = await request.post<{
|
const response = await request.post<{
|
||||||
upload_id?: string;
|
upload_id?: string;
|
||||||
current_size?: number;
|
current_size?: number;
|
||||||
|
|
@ -408,16 +573,42 @@ export const useLegacyUploadFile = () => {
|
||||||
(overallProgress / file.size) * 100,
|
(overallProgress / file.size) * 100,
|
||||||
);
|
);
|
||||||
setUploadProgress(Math.min(99, percentCompleted));
|
setUploadProgress(Math.min(99, percentCompleted));
|
||||||
|
|
||||||
|
if (
|
||||||
|
percentCompleted >= lastLoggedProgressRef.current + 5 ||
|
||||||
|
percentCompleted === 100
|
||||||
|
) {
|
||||||
|
lastLoggedProgressRef.current = percentCompleted;
|
||||||
|
console.info("[App2Client][LegacyUpload] Прогресс загрузки", {
|
||||||
|
fileName: file.name,
|
||||||
|
percent: percentCompleted,
|
||||||
|
loaded: overallProgress,
|
||||||
|
total: file.size,
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!uploadId && response.data.upload_id) {
|
if (!uploadId && response.data.upload_id) {
|
||||||
uploadId = response.data.upload_id;
|
uploadId = response.data.upload_id;
|
||||||
|
console.info("[App2Client][LegacyUpload] Получили upload_id", {
|
||||||
|
fileName: file.name,
|
||||||
|
uploadId,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.data.content_id) {
|
if (response.data.content_id) {
|
||||||
setUploadProgress(100);
|
setUploadProgress(100);
|
||||||
|
|
||||||
|
console.info("[App2Client][LegacyUpload] Сервер вернул итоговые данные", {
|
||||||
|
fileName: file.name,
|
||||||
|
uploadId,
|
||||||
|
hasContentId: Boolean(
|
||||||
|
response.data.content_id_v1 || response.data.content_id,
|
||||||
|
),
|
||||||
|
hasSha256: Boolean(response.data.content_sha256),
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content_sha256: response.data.content_sha256 || "",
|
content_sha256: response.data.content_sha256 || "",
|
||||||
content_id_v1:
|
content_id_v1:
|
||||||
|
|
@ -428,9 +619,18 @@ export const useLegacyUploadFile = () => {
|
||||||
|
|
||||||
if (response.data.current_size !== undefined) {
|
if (response.data.current_size !== undefined) {
|
||||||
offset = response.data.current_size;
|
offset = response.data.current_size;
|
||||||
|
console.info("[App2Client][LegacyUpload] Продолжаем загрузку", {
|
||||||
|
fileName: file.name,
|
||||||
|
nextOffset: offset,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
const error = new Error("Missing current_size in response");
|
const error = new Error("Missing current_size in response");
|
||||||
setUploadError(error);
|
setUploadError(error);
|
||||||
|
lastError = error;
|
||||||
|
console.error("[App2Client][LegacyUpload] Сервер не вернул current_size", {
|
||||||
|
fileName: file.name,
|
||||||
|
response,
|
||||||
|
});
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -439,6 +639,10 @@ export const useLegacyUploadFile = () => {
|
||||||
"All chunks uploaded but server did not return content_id",
|
"All chunks uploaded but server did not return content_id",
|
||||||
);
|
);
|
||||||
setUploadError(error);
|
setUploadError(error);
|
||||||
|
lastError = error;
|
||||||
|
console.error("[App2Client][LegacyUpload] Все чанки отправлены, но content_id отсутствует", {
|
||||||
|
fileName: file.name,
|
||||||
|
});
|
||||||
throw error;
|
throw error;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const normalized = normalizeError(
|
const normalized = normalizeError(
|
||||||
|
|
@ -447,9 +651,18 @@ export const useLegacyUploadFile = () => {
|
||||||
);
|
);
|
||||||
setUploadError(normalized);
|
setUploadError(normalized);
|
||||||
setIsUploading(false);
|
setIsUploading(false);
|
||||||
|
lastError = normalized;
|
||||||
|
console.error("[App2Client][LegacyUpload] Ошибка во время загрузки", {
|
||||||
|
fileName: file.name,
|
||||||
|
error: normalized,
|
||||||
|
});
|
||||||
throw normalized;
|
throw normalized;
|
||||||
} finally {
|
} finally {
|
||||||
setIsUploading(false);
|
setIsUploading(false);
|
||||||
|
console.info("[App2Client][LegacyUpload] Завершили работу загрузчика", {
|
||||||
|
fileName: file.name,
|
||||||
|
hasError: Boolean(lastError),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue