From 12f46f572dd47f9504c08fcae7f7718dc7243b12 Mon Sep 17 00:00:00 2001 From: lendry Date: Tue, 7 Jul 2026 10:29:07 +0300 Subject: [PATCH] fix and update --- apps/frontend/app/auth/login/page.tsx | 6 +- apps/frontend/app/download/android/route.ts | 2 +- .../downloads/android/[releaseId]/route.ts | 9 +++ apps/frontend/app/downloads/android/route.ts | 13 +---- .../downloads/windows/[releaseId]/route.ts | 9 +++ apps/frontend/app/downloads/windows/route.ts | 13 +---- apps/frontend/components/id/auth-provider.tsx | 8 ++- .../frontend/components/id/pin-lock-modal.tsx | 28 ++++++++-- apps/frontend/lib/api.ts | 3 +- apps/frontend/lib/proxy-release-download.ts | 55 +++++++++++++++++++ 10 files changed, 115 insertions(+), 31 deletions(-) create mode 100644 apps/frontend/app/downloads/android/[releaseId]/route.ts create mode 100644 apps/frontend/app/downloads/windows/[releaseId]/route.ts create mode 100644 apps/frontend/lib/proxy-release-download.ts diff --git a/apps/frontend/app/auth/login/page.tsx b/apps/frontend/app/auth/login/page.tsx index 9b664a7..34ed0c8 100644 --- a/apps/frontend/app/auth/login/page.tsx +++ b/apps/frontend/app/auth/login/page.tsx @@ -1520,13 +1520,13 @@ function LoginPageContent() { - Скачать приложение - + diff --git a/apps/frontend/app/download/android/route.ts b/apps/frontend/app/download/android/route.ts index 749796b..53400b9 100644 --- a/apps/frontend/app/download/android/route.ts +++ b/apps/frontend/app/download/android/route.ts @@ -3,6 +3,6 @@ import { NextRequest, NextResponse } from 'next/server'; export const runtime = 'nodejs'; export async function GET(request: NextRequest) { - const target = new URL('/downloads/android', request.nextUrl.origin); + const target = new URL('/downloads', request.nextUrl.origin); return NextResponse.redirect(target, 302); } diff --git a/apps/frontend/app/downloads/android/[releaseId]/route.ts b/apps/frontend/app/downloads/android/[releaseId]/route.ts new file mode 100644 index 0000000..0da2883 --- /dev/null +++ b/apps/frontend/app/downloads/android/[releaseId]/route.ts @@ -0,0 +1,9 @@ +import type { NextRequest } from 'next/server'; +import { proxyReleaseDownload } from '@/lib/proxy-release-download'; + +export const runtime = 'nodejs'; + +export async function GET(request: NextRequest, context: { params: Promise<{ releaseId: string }> }) { + const { releaseId } = await context.params; + return proxyReleaseDownload(request, 'android', releaseId); +} diff --git a/apps/frontend/app/downloads/android/route.ts b/apps/frontend/app/downloads/android/route.ts index dab1cf0..383af52 100644 --- a/apps/frontend/app/downloads/android/route.ts +++ b/apps/frontend/app/downloads/android/route.ts @@ -1,15 +1,8 @@ -import { NextRequest, NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; +import { proxyReleaseDownload } from '@/lib/proxy-release-download'; export const runtime = 'nodejs'; -function resolveApiBase(request: NextRequest) { - const internal = process.env.INTERNAL_API_URL?.replace(/\/$/, ''); - if (internal) return internal; - const origin = request.nextUrl.origin; - return `${origin}/idp-api`; -} - export async function GET(request: NextRequest) { - const target = `${resolveApiBase(request)}/releases/download/android`; - return NextResponse.redirect(target, 302); + return proxyReleaseDownload(request, 'android'); } diff --git a/apps/frontend/app/downloads/windows/[releaseId]/route.ts b/apps/frontend/app/downloads/windows/[releaseId]/route.ts new file mode 100644 index 0000000..f43940f --- /dev/null +++ b/apps/frontend/app/downloads/windows/[releaseId]/route.ts @@ -0,0 +1,9 @@ +import type { NextRequest } from 'next/server'; +import { proxyReleaseDownload } from '@/lib/proxy-release-download'; + +export const runtime = 'nodejs'; + +export async function GET(request: NextRequest, context: { params: Promise<{ releaseId: string }> }) { + const { releaseId } = await context.params; + return proxyReleaseDownload(request, 'windows', releaseId); +} diff --git a/apps/frontend/app/downloads/windows/route.ts b/apps/frontend/app/downloads/windows/route.ts index d827a0d..4ed5c82 100644 --- a/apps/frontend/app/downloads/windows/route.ts +++ b/apps/frontend/app/downloads/windows/route.ts @@ -1,15 +1,8 @@ -import { NextRequest, NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; +import { proxyReleaseDownload } from '@/lib/proxy-release-download'; export const runtime = 'nodejs'; -function resolveApiBase(request: NextRequest) { - const internal = process.env.INTERNAL_API_URL?.replace(/\/$/, ''); - if (internal) return internal; - const origin = request.nextUrl.origin; - return `${origin}/idp-api`; -} - export async function GET(request: NextRequest) { - const target = `${resolveApiBase(request)}/releases/download/windows`; - return NextResponse.redirect(target, 302); + return proxyReleaseDownload(request, 'windows'); } diff --git a/apps/frontend/components/id/auth-provider.tsx b/apps/frontend/components/id/auth-provider.tsx index 97434a2..c533c4d 100644 --- a/apps/frontend/components/id/auth-provider.tsx +++ b/apps/frontend/components/id/auth-provider.tsx @@ -806,7 +806,13 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { ) : ( children )} - + Promise; + onLogout?: () => void; } -export function PinLockModal({ open, isSubmitting, error, onSubmit }: PinLockModalProps) { +export function PinLockModal({ open, isSubmitting, error, onSubmit, onLogout }: PinLockModalProps) { const [pin, setPin] = React.useState(''); React.useEffect(() => { @@ -30,13 +31,32 @@ export function PinLockModal({ open, isSubmitting, error, onSubmit }: PinLockMod return ( undefined}> - event.preventDefault()} onEscapeKeyDown={(event) => event.preventDefault()}> + event.preventDefault()} + onEscapeKeyDown={(event) => event.preventDefault()} + > + {onLogout ? ( + + ) : null} +
Введите PIN-код -

Сессия заблокирована из-за бездействия. Подтвердите PIN, чтобы продолжить работу.

+

+ Сессия заблокирована из-за бездействия. Подтвердите PIN, чтобы продолжить работу. +

diff --git a/apps/frontend/lib/api.ts b/apps/frontend/lib/api.ts index 2190cf8..6a8d8ca 100644 --- a/apps/frontend/lib/api.ts +++ b/apps/frontend/lib/api.ts @@ -1769,9 +1769,8 @@ export async function checkAppUpdate(platform: AppReleasePlatform, versionCode: } export function buildAppReleaseDownloadUrl(platform: AppReleasePlatform, releaseId?: string) { - const base = getApiUrl().replace(/\/$/, ''); const slug = platform.toLowerCase(); - return releaseId ? `${base}/releases/download/${slug}/${releaseId}` : `${base}/releases/download/${slug}`; + return releaseId ? `/downloads/${slug}/${releaseId}` : `/downloads/${slug}`; } export async function fetchAdminAppReleases(token: string, platform?: AppReleasePlatform) { diff --git a/apps/frontend/lib/proxy-release-download.ts b/apps/frontend/lib/proxy-release-download.ts new file mode 100644 index 0000000..2130dfb --- /dev/null +++ b/apps/frontend/lib/proxy-release-download.ts @@ -0,0 +1,55 @@ +import type { NextRequest } from 'next/server'; + +function resolveServerApiBase(request: NextRequest) { + const internal = process.env.INTERNAL_API_URL?.replace(/\/$/, ''); + if (internal) return internal; + return `${request.nextUrl.origin}/idp-api`; +} + +const PASSTHROUGH_HEADERS = [ + 'content-type', + 'content-length', + 'content-disposition', + 'x-release-version', + 'x-release-version-code', + 'x-release-sha256', + 'cache-control' +] as const; + +export async function proxyReleaseDownload( + request: NextRequest, + platform: 'android' | 'windows', + releaseId?: string +) { + const apiBase = resolveServerApiBase(request); + const path = releaseId + ? `/releases/download/${platform}/${releaseId}` + : `/releases/download/${platform}`; + + const upstream = await fetch(`${apiBase}${path}`, { + method: 'GET', + cache: 'no-store' + }); + + if (!upstream.ok) { + const message = await upstream.text().catch(() => 'Файл релиза недоступен'); + return new Response(message, { + status: upstream.status, + headers: { + 'Content-Type': 'text/plain; charset=utf-8', + 'Cache-Control': 'no-store' + } + }); + } + + const headers = new Headers(); + for (const key of PASSTHROUGH_HEADERS) { + const value = upstream.headers.get(key); + if (value) headers.set(key, value); + } + + return new Response(upstream.body, { + status: upstream.status, + headers + }); +}