diff --git a/apps/frontend/app/admin/releases/page.tsx b/apps/frontend/app/admin/releases/page.tsx index 9e55914..1b974f8 100644 --- a/apps/frontend/app/admin/releases/page.tsx +++ b/apps/frontend/app/admin/releases/page.tsx @@ -18,6 +18,7 @@ import { const ANDROID_PLATFORM = 'ANDROID' as const; const APK_ACCEPT = '.apk,application/vnd.android.package-archive'; +const MAX_RELEASE_FILE_BYTES = 350 * 1024 * 1024; function formatBytes(value: string) { const size = Number(value); @@ -83,6 +84,10 @@ export default function AdminReleasesPage() { showToast('Выберите файл релиза'); return; } + if (file.size > MAX_RELEASE_FILE_BYTES) { + showToast('APK не должен превышать 350 МБ'); + return; + } const parsedVersionCode = Number(versionCode); if (!version.trim() || !Number.isInteger(parsedVersionCode) || parsedVersionCode < 1) { showToast('Укажите версию и положительный код версии'); diff --git a/apps/frontend/lib/api.ts b/apps/frontend/lib/api.ts index 6a8d8ca..a6dda47 100644 --- a/apps/frontend/lib/api.ts +++ b/apps/frontend/lib/api.ts @@ -1809,6 +1809,13 @@ export async function uploadAdminAppRelease( if (!response.ok) { const body = await response.json().catch(() => ({})); + if (response.status === 413) { + throw new ApiError( + 'Файл слишком большой для загрузки (лимит сервера). Максимальный размер APK — 350 МБ. Попросите администратора обновить nginx: ./install.sh --fix-proxy', + 413, + 'UPLOAD_TOO_LARGE' + ); + } const message = typeof body?.message === 'string' ? body.message : 'Не удалось загрузить релиз'; throw new ApiError(message, response.status, 'UPLOAD_FAILED'); } diff --git a/install.sh b/install.sh index 17506c0..cae1bce 100644 --- a/install.sh +++ b/install.sh @@ -2306,6 +2306,9 @@ generate_all_self_signed_certs() { ok "Самоподписанный сертификат создан: ${LOCAL_CERTS_DIR}/shared-intranet" } +# Лимит тела запроса в nginx (APK до 350 МБ + запас на multipart). +NGINX_RELEASE_UPLOAD_BODY_SIZE="${NGINX_RELEASE_UPLOAD_BODY_SIZE:-512m}" + nginx_conf_target() { local label="$1" if [[ "$NGINX_MODE" == "docker" ]]; then @@ -2571,6 +2574,10 @@ ${proxy_ws} } location ^~ /idp-api/ { + client_max_body_size ${NGINX_RELEASE_UPLOAD_BODY_SIZE}; + proxy_read_timeout 900s; + proxy_send_timeout 900s; + client_body_timeout 900s; ${proxy_idp} ${nginx_proxy_headers_api} } @@ -2611,7 +2618,7 @@ server { listen 80; listen [::]:80; server_name ${domain}; - client_max_body_size 100m; + client_max_body_size ${NGINX_RELEASE_UPLOAD_BODY_SIZE}; ${server_dns} ${ws_block} ${api_location} @@ -2626,7 +2633,7 @@ ${http_preamble}server { listen [::]:443 ssl http2; server_name ${domain}; ${ssl_block} - client_max_body_size 100m; + client_max_body_size ${NGINX_RELEASE_UPLOAD_BODY_SIZE}; ${server_dns} ${ws_block} ${api_location} @@ -2661,6 +2668,10 @@ ${proxy_ws} } location ^~ /idp-api/ { + client_max_body_size ${NGINX_RELEASE_UPLOAD_BODY_SIZE}; + proxy_read_timeout 900s; + proxy_send_timeout 900s; + client_body_timeout 900s; ${proxy_idp} ${nginx_proxy_headers_api} } @@ -2709,6 +2720,7 @@ server { listen 80; listen [::]:80; server_name ${domain}; + client_max_body_size ${NGINX_RELEASE_UPLOAD_BODY_SIZE}; ${server_dns} ${idp_api_block} location / { @@ -2726,7 +2738,7 @@ ${http_preamble}server { listen [::]:443 ssl http2; server_name ${domain}; ${ssl_block} - client_max_body_size 50m; + client_max_body_size ${NGINX_RELEASE_UPLOAD_BODY_SIZE}; ${server_dns} ${idp_api_block} location / { @@ -2761,7 +2773,7 @@ server { listen 80; listen [::]:80; server_name ${domain}; - client_max_body_size 50m; + client_max_body_size ${NGINX_RELEASE_UPLOAD_BODY_SIZE}; ${server_dns} ${idp_api_block} location / { @@ -2779,7 +2791,7 @@ ${http_preamble}server { listen [::]:443 ssl http2; server_name ${domain}; ${ssl_block} - client_max_body_size 50m; + client_max_body_size ${NGINX_RELEASE_UPLOAD_BODY_SIZE}; ${server_dns} ${idp_api_block} location / {