add upload logs

This commit is contained in:
user 2025-10-01 23:18:04 +03:00
parent 214ac28926
commit 9449a45ef9
3 changed files with 313 additions and 13 deletions

View File

@ -42,18 +42,38 @@ export const PresubmitStep = ({ prevStep }: PresubmitStepProps) => {
setIsErrorUploadModal(false);
uploadFile.resetUploadError();
uploadCover.resetUploadError();
console.info("[App2Client][Presubmit] Пользователь закрыл модалку ошибок загрузки");
};
useEffect(() => {
if (uploadFile.uploadError || uploadCover.uploadError) {
console.error("[App2Client][Presubmit] Обнаружены ошибки загрузки", {
fileError: uploadFile.uploadError,
coverError: uploadCover.uploadError,
});
setIsErrorUploadModal(true);
}
}, [uploadFile.uploadError, uploadCover.uploadError]);
const handleSubmit = async () => {
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: "" };
console.info("[App2Client][Presubmit] Загружаем основной файл через tus");
const fileUploadResult = await uploadFile.mutateAsync({
file: rootStore.file as File,
metadata: {
@ -70,12 +90,37 @@ export const PresubmitStep = ({ prevStep }: PresubmitStepProps) => {
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) {
console.info("[App2Client][Presubmit] Начинаем загрузку обложки", {
fileName: rootStore.cover.name,
fileSize: rootStore.cover.size,
fileType: rootStore.cover.type,
});
coverUploadResult = await uploadCover.mutateAsync(
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({
title: rootStore.name,
@ -112,6 +157,10 @@ export const PresubmitStep = ({ prevStep }: PresubmitStepProps) => {
if (createContentResponse.data) {
if (createContentResponse.data.address != "free") {
console.info("[App2Client][Presubmit] Отправляем транзакцию через TonConnect", {
address: createContentResponse.data.address,
amount: createContentResponse.data.amount,
});
const transactionResponse = await tonConnectUI.sendTransaction({
validUntil: Math.floor(Date.now() / 1000) + 120,
messages: [
@ -123,19 +172,29 @@ export const PresubmitStep = ({ prevStep }: PresubmitStepProps) => {
],
});
if (transactionResponse.boc) {
console.info("[App2Client][Presubmit] Транзакция успешно отправлена", {
bocLength: transactionResponse.boc.length,
});
WebApp.close();
} else {
console.error("Transaction failed:", transactionResponse);
console.error("[App2Client][Presubmit] Транзакция не отправлена", {
response: transactionResponse,
});
}
}
}
console.info("[App2Client][Presubmit] Завершаем процесс и закрываем WebApp");
WebApp.close();
// @ts-expect-error Type issues
} catch (error: never) {
console.error("An error occurred during the submission process:", error);
console.error("[App2Client][Presubmit] Ошибка во время отправки", {
error,
});
if (error?.status === 400) {
alert(

View File

@ -20,16 +20,44 @@ type UseCreateNewContentPayload = {
export const useCreateNewContent = () => {
return useMutation(
["create-new-content"],
(payload: UseCreateNewContentPayload) => {
return request.post<{
address: string;
amount: string;
payload: string;
}>("/blockchain.sendNewContentMessage", payload, {
headers: {
Authorization: localStorage.getItem('auth_v1_token') ?? ""
}
async (payload: UseCreateNewContentPayload) => {
console.info("[App2Client][Content] Отправляем контент на создание", {
title: payload.title,
hasImage: Boolean(payload.image),
contentCid: payload.content,
hashtags: payload.hashtags,
authorsCount: payload.authors.length,
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;
}
},
);
};

View File

@ -94,8 +94,16 @@ const pollUploadStatus = async (
signal?: AbortSignal,
): Promise<UploadStatusResponse> => {
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) {
attempt += 1;
if (signal?.aborted) {
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") {
throw new Error(data.error || "Tus upload failed on server");
}
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;
}
} catch (error) {
const maybeAxios = error as { response?: { status?: number } };
if (maybeAxios?.response?.status === 404) {
// 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") {
console.warn("[App2Client][TusUpload] Поллинг tus прерван", {
uploadId,
attempt,
});
throw error;
} else {
console.error("[App2Client][TusUpload] Ошибка во время polling", {
uploadId,
attempt,
error,
});
throw normalizeError(error, "Tus status polling failed");
}
}
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");
}
@ -153,6 +194,7 @@ export const useTusUpload = () => {
const [isUploading, setIsUploading] = useState(false);
const [uploadError, setUploadError] = useState<Error | null>(null);
const activeUploadRef = useRef<Upload | null>(null);
const lastLoggedProgressRef = useRef(-10);
const mutation = useMutation<TusUploadResult, Error, TusUploadArgs>(
["upload-file", "tus"],
@ -161,15 +203,16 @@ export const useTusUpload = () => {
throw new Error("File is required for tus upload");
}
if (!TUS_ENDPOINT) {
throw new Error("Tus endpoint is not configured");
}
setIsUploading(true);
setUploadProgress(0);
setUploadError(null);
let lastError: Error | null = null;
try {
if (!TUS_ENDPOINT) {
throw new Error("Tus endpoint is not configured");
}
const result = await new Promise<TusUploadResult>((resolve, reject) => {
const token = localStorage.getItem("auth_v1_token") ?? "";
const normalizedMeta = normalizeMetadata(metadata);
@ -178,6 +221,13 @@ export const useTusUpload = () => {
headers.Authorization = token;
}
console.info("[App2Client][TusUpload] Начинаем загрузку", {
fileName: file.name,
fileSize: file.size,
fileType: file.type,
metadata: normalizedMeta,
});
const upload = new Upload(file, {
endpoint: TUS_ENDPOINT,
metadata: {
@ -191,6 +241,7 @@ export const useTusUpload = () => {
uploadDataDuringCreation: true,
onError: (err) => {
const normalized = normalizeError(err, "Tus upload failed");
lastError = normalized;
setUploadError(normalized);
setIsUploading(false);
setUploadProgress(0);
@ -202,6 +253,16 @@ export const useTusUpload = () => {
? Math.floor((bytesUploaded / bytesTotal) * 100)
: 0;
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: () => {
(async () => {
@ -213,6 +274,10 @@ export const useTusUpload = () => {
}
const uploadId = extractUploadId(uploadUrl);
console.info("[App2Client][TusUpload] Загрузка завершена на tus, начинаем финализацию", {
fileName: file.name,
uploadId,
});
const status = await pollUploadStatus(uploadId, signal);
const encryptedCid = status.encrypted_cid;
if (!encryptedCid) {
@ -223,6 +288,14 @@ export const useTusUpload = () => {
setIsUploading(false);
activeUploadRef.current = null;
console.info("[App2Client][TusUpload] Финализация tus завершена", {
fileName: file.name,
uploadId,
encryptedCid,
finalState: status.state,
sizeBytes: status.size_bytes,
});
resolve({
kind: "tus",
uploadId,
@ -235,6 +308,11 @@ export const useTusUpload = () => {
statusError,
"Failed to finalize tus upload",
);
lastError = normalized;
console.error("[App2Client][TusUpload] Ошибка финализации tus", {
fileName: file.name,
error: normalized,
});
setUploadError(normalized);
setIsUploading(false);
activeUploadRef.current = null;
@ -245,6 +323,11 @@ export const useTusUpload = () => {
});
activeUploadRef.current = upload;
lastLoggedProgressRef.current = 0;
console.info("[App2Client][TusUpload] Запрашиваем предыдущие загрузки для возобновления", {
fileName: file.name,
});
if (signal) {
signal.addEventListener(
@ -255,6 +338,10 @@ export const useTusUpload = () => {
"Tus upload aborted",
"AbortError",
);
console.warn("[App2Client][TusUpload] Загрузка прервана внешним сигналом", {
fileName: file.name,
});
lastError = abortError;
setUploadError(abortError);
setIsUploading(false);
activeUploadRef.current = null;
@ -267,16 +354,32 @@ export const useTusUpload = () => {
upload
.findPreviousUploads()
.then((previousUploads) => {
console.info("[App2Client][TusUpload] Результат поиска предыдущих загрузок", {
fileName: file.name,
count: previousUploads.length,
});
if (previousUploads.length > 0) {
upload.resumeFromPreviousUpload(previousUploads[0]);
console.info("[App2Client][TusUpload] Возобновляем загрузку", {
fileName: file.name,
});
}
upload.start();
console.info("[App2Client][TusUpload] Стартуем загрузку", {
fileName: file.name,
chunkSize: upload.options.chunkSize,
});
})
.catch((resumeError) => {
const normalized = normalizeError(
resumeError,
"Failed to resume tus upload",
);
lastError = normalized;
console.error("[App2Client][TusUpload] Не удалось возобновить загрузку", {
fileName: file.name,
error: normalized,
});
setUploadError(normalized);
setIsUploading(false);
activeUploadRef.current = null;
@ -285,9 +388,25 @@ export const useTusUpload = () => {
});
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 {
activeUploadRef.current = null;
setIsUploading(false);
console.info("[App2Client][TusUpload] Завершили работу загрузчика", {
fileName: file?.name,
hasError: Boolean(lastError),
});
}
},
);
@ -300,6 +419,9 @@ export const useTusUpload = () => {
activeUploadRef.current = null;
setIsUploading(false);
setUploadProgress(0);
console.warn("[App2Client][TusUpload] Принудительно остановили загрузку", {
reason: "manual",
});
}
};
@ -317,11 +439,21 @@ export const useLegacyUploadFile = () => {
const [uploadProgress, setUploadProgress] = useState(0);
const [isUploading, setIsUploading] = useState(false);
const [uploadError, setUploadError] = useState<Error | null>(null);
const lastLoggedProgressRef = useRef(-10);
const mutation = useMutation(["upload-file", "legacy"], async (file: File) => {
setIsUploading(true);
setUploadProgress(0);
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 {
if (file.size <= MAX_CHUNK_SIZE) {
@ -352,11 +484,35 @@ export const useLegacyUploadFile = () => {
(progressEvent.loaded * 100) / total,
);
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);
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 {
content_sha256: response.data.content_sha256 || "",
content_id_v1:
@ -392,6 +548,15 @@ export const useLegacyUploadFile = () => {
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<{
upload_id?: string;
current_size?: number;
@ -408,16 +573,42 @@ export const useLegacyUploadFile = () => {
(overallProgress / file.size) * 100,
);
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) {
uploadId = response.data.upload_id;
console.info("[App2Client][LegacyUpload] Получили upload_id", {
fileName: file.name,
uploadId,
});
}
if (response.data.content_id) {
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 {
content_sha256: response.data.content_sha256 || "",
content_id_v1:
@ -428,9 +619,18 @@ export const useLegacyUploadFile = () => {
if (response.data.current_size !== undefined) {
offset = response.data.current_size;
console.info("[App2Client][LegacyUpload] Продолжаем загрузку", {
fileName: file.name,
nextOffset: offset,
});
} else {
const error = new Error("Missing current_size in response");
setUploadError(error);
lastError = error;
console.error("[App2Client][LegacyUpload] Сервер не вернул current_size", {
fileName: file.name,
response,
});
throw error;
}
}
@ -439,6 +639,10 @@ export const useLegacyUploadFile = () => {
"All chunks uploaded but server did not return content_id",
);
setUploadError(error);
lastError = error;
console.error("[App2Client][LegacyUpload] Все чанки отправлены, но content_id отсутствует", {
fileName: file.name,
});
throw error;
} catch (error) {
const normalized = normalizeError(
@ -447,9 +651,18 @@ export const useLegacyUploadFile = () => {
);
setUploadError(normalized);
setIsUploading(false);
lastError = normalized;
console.error("[App2Client][LegacyUpload] Ошибка во время загрузки", {
fileName: file.name,
error: normalized,
});
throw normalized;
} finally {
setIsUploading(false);
console.info("[App2Client][LegacyUpload] Завершили работу загрузчика", {
fileName: file.name,
hasError: Boolean(lastError),
});
}
});