feat: RSS-SE-24 implement request analytics collection - #37
dilmun1101 merged 2 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughДобавлено сохранение успешных и ошибочных прокси-запросов в Supabase. Защищённая страница загружает историю текущего пользователя и отображает метаданные запросов, размеры ответов, длительность и детали ошибок. ChangesИстория запросов
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant Proxy
participant Supabase
participant HistoryPage
Client->>Proxy: Выполняет прокси-запрос
Proxy->>Supabase: Получает пользователя
Proxy->>Supabase: Сохраняет запись history
Proxy-->>Client: Возвращает результат
HistoryPage->>Supabase: Загружает историю пользователя
Supabase-->>HistoryPage: Возвращает записи
HistoryPage-->>Client: Отображает историю
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/api/proxy/route.ts`:
- Around line 45-47: Измените обработку ответа в маршруте прокси вокруг
response.arrayBuffer(): не буферизуйте тело целиком перед отправкой клиенту, а
возвращайте исходный response.body для сохранения потоковой передачи. Для
responseSize используйте валидный Content-Length либо null; если подсчёт байтов
через поток необходим, примените поток без нарушения передачи ответа клиенту и
сохраните расчёт duration.
- Around line 49-65: Move the history analytics block around
supabase.auth.getUser() and the history insert out of the proxy response’s
critical path by scheduling it with the framework’s after() or equivalent
best-effort task before returning. Preserve the existing analytics fields, and
catch and log errors from both getUser() and insert() so RLS or schema failures
are visible without delaying or failing the proxy response.
In `@src/features/history/api/history.ts`:
- Around line 19-23: Ограничьте запрос истории в функции API вокруг цепочки
Supabase для таблицы history фиксированным размером страницы и добавьте
курсорную пагинацию по timestamp (с id как стабильным tie-breaker). Принимайте
курсор и размер страницы из параметров запроса, применяйте соответствующий
фильтр до сортировки, запрашивайте на одну запись больше для определения
следующей страницы и возвращайте данные вместе с next cursor.
In `@src/pages/history-page/ui/history-page.tsx`:
- Around line 18-45: Update the history page component and its caller to receive
the active route locale, replace hardcoded labels with the existing translation
mechanism, and pass that locale to Date.toLocaleString for timestamp formatting.
Preserve the current empty-state, item rendering, and conditional error display
behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 5e9d6222-ca0c-4046-8de8-cc3acdea9572
📒 Files selected for processing (5)
app/[locale]/history/page.tsxapp/api/proxy/route.tssrc/features/history/api/history.tssrc/pages/history-page/index.tssrc/pages/history-page/ui/history-page.tsx
| const responseBuffer = await response.arrayBuffer(); | ||
| const duration = Date.now() - startedAt; | ||
| const responseSize = responseBuffer.byteLength; |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
Не буферизуйте весь ответ прокси.
response.arrayBuffer() удерживает в памяти весь ответ произвольного удалённого URL до начала передачи клиенту. Большие ответы отключат стриминг и могут исчерпать память обработчика. Считайте байты через поток либо сохраняйте валидный Content-Length/null, но возвращайте исходный поток ответа.
Also applies to: 68-72
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/api/proxy/route.ts` around lines 45 - 47, Измените обработку ответа в
маршруте прокси вокруг response.arrayBuffer(): не буферизуйте тело целиком перед
отправкой клиенту, а возвращайте исходный response.body для сохранения потоковой
передачи. Для responseSize используйте валидный Content-Length либо null; если
подсчёт байтов через поток необходим, примените поток без нарушения передачи
ответа клиенту и сохраните расчёт duration.
| const supabase = await createClient(); | ||
| const { | ||
| data: { user }, | ||
| } = await supabase.auth.getUser(); | ||
|
|
||
| if (user) { | ||
| await supabase.from('history').insert({ | ||
| user_id: user.id, | ||
| url: targetUrl, | ||
| method, | ||
| status: response.status, | ||
| duration, | ||
| request_size: requestSize, | ||
| response_size: responseSize, | ||
| timestamp: new Date().toISOString(), | ||
| error_details: response.ok ? null : `HTTP ${response.status}`, | ||
| }); |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
rg -n -C 2 '"next"|`@supabase/supabase-js`' package.json 2>/dev/null || true
rg -n -C 4 "supabase\.auth\.getUser|\.from\('history'\)\.insert" app/api/proxy/route.ts
curl -fsSL https://nextjs.org/docs/app/api-reference/functions/after \
| grep -i -m 3 'route handler'
curl -fsSL https://supabase.com/docs/reference/javascript/insert \
| grep -i -m 3 'error'Repository: FierceSloth/swagger-editor-app
Length of output: 50388
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '\n== app/api/proxy/route.ts ==\n'
wc -l app/api/proxy/route.ts
sed -n '1,220p' app/api/proxy/route.ts
printf '\n== поиски after / background / history insert ==\n'
rg -n "after\\(|supabase\\.auth\\.getUser\\(|from\\('history'\\)\\.insert|AbortController|timeout" app/api/proxy app -g '!**/node_modules/**' || trueRepository: FierceSloth/swagger-editor-app
Length of output: 4877
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '\n== Next.js after docs snippet ==\n'
python3 - <<'PY'
import urllib.request, re
url = 'https://nextjs.org/docs/app/api-reference/functions/after'
html = urllib.request.urlopen(url, timeout=20).read().decode('utf-8', 'ignore')
for pat in [
r'Route Handlers',
r'after\(',
r'can be used in Route Handlers',
r'cannot be used in Route Handlers',
]:
m = re.search(pat, html, re.I)
print(pat, '=>', bool(m))
PY
printf '\n== Supabase insert docs snippet ==\n'
python3 - <<'PY'
import urllib.request, re
url = 'https://supabase.com/docs/reference/javascript/insert'
html = urllib.request.urlopen(url, timeout=20).read().decode('utf-8', 'ignore')
for pat in [r'error', r'data', r'select\(', r'RLS', r'row level security']:
m = re.search(pat, html, re.I)
print(pat, '=>', bool(m))
PYRepository: FierceSloth/swagger-editor-app
Length of output: 2796
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '\n== app/api/proxy/route.ts line numbers ==\n'
nl -ba app/api/proxy/route.ts | sed -n '1,180p'
printf '\n== package.json relevant deps ==\n'
nl -ba package.json | sed -n '1,90p'
printf '\n== search for after import/use in repo ==\n'
rg -n "from 'next/server'|from \"next/server\"|\\bafter\\b" app lib src . -g '!**/node_modules/**' || trueRepository: FierceSloth/swagger-editor-app
Length of output: 251
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
python3 - <<'PY'
import urllib.request, re
html = urllib.request.urlopen('https://nextjs.org/docs/app/api-reference/functions/after', timeout=20).read().decode('utf-8', 'ignore')
text = re.sub(r'<script\b.*?</script>|<style\b.*?</style>', ' ', html, flags=re.S|re.I)
text = re.sub(r'<[^>]+>', ' ', text)
text = re.sub(r'\s+', ' ', text)
for needle in [
'Route Handlers',
'can be used in Server Components, Server Actions, Route Handlers, and Middleware',
'after is a function that allows you to schedule work to be done after a response has been sent',
'after callback',
]:
idx = text.lower().find(needle.lower())
print(f'--- {needle} ---')
print(text[idx-250:idx+600] if idx != -1 else 'NOT FOUND')
PYRepository: FierceSloth/swagger-editor-app
Length of output: 2762
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
python3 - <<'PY'
import urllib.request, re
html = urllib.request.urlopen('https://supabase.com/docs/reference/javascript/insert', timeout=20).read().decode('utf-8', 'ignore')
text = re.sub(r'<script\b.*?</script>|<style\b.*?</style>', ' ', html, flags=re.S|re.I)
text = re.sub(r'<[^>]+>', ' ', text)
text = re.sub(r'\s+', ' ', text)
for needle in [
'If you do not select',
'errors',
'RLS',
'row level security',
'returns',
]:
idx = text.lower().find(needle.lower())
print(f'--- {needle} ---')
print(text[idx-250:idx+600] if idx != -1 else 'NOT FOUND')
PYRepository: FierceSloth/swagger-editor-app
Length of output: 2762
Вынесите аналитику из критического пути прокси.
В обеих ветках getUser() и insert() выполняются до return, поэтому задержка Supabase тормозит ответ; текущий AbortController это не покрывает. Ошибки insert() тоже игнорируются, так что RLS/схема могут молча терять историю. Перенесите запись в after()/best-effort задачу и логируйте ошибки записи.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/api/proxy/route.ts` around lines 49 - 65, Move the history analytics
block around supabase.auth.getUser() and the history insert out of the proxy
response’s critical path by scheduling it with the framework’s after() or
equivalent best-effort task before returning. Preserve the existing analytics
fields, and catch and log errors from both getUser() and insert() so RLS or
schema failures are visible without delaying or failing the proxy response.
| const { data, error } = await supabase | ||
| .from('history') | ||
| .select('id, user_id, url, method, status, duration, timestamp, request_size, response_size, error_details') | ||
| .eq('user_id', userId) | ||
| .order('timestamp', { ascending: false }); |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift
Ограничьте и пагинируйте историю.
Запрос загружает все записи пользователя, а прокси создаёт запись для каждого успешного и ошибочного вызова. Со временем страница будет читать, сериализовать и рендерить неограниченный объём данных. Добавьте размер страницы и курсорную пагинацию.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/features/history/api/history.ts` around lines 19 - 23, Ограничьте запрос
истории в функции API вокруг цепочки Supabase для таблицы history фиксированным
размером страницы и добавьте курсорную пагинацию по timestamp (с id как
стабильным tie-breaker). Принимайте курсор и размер страницы из параметров
запроса, применяйте соответствующий фильтр до сортировки, запрашивайте на одну
запись больше для определения следующей страницы и возвращайте данные вместе с
next cursor.
| <h1>History</h1> | ||
| <p>You haven't executed any requests yet.</p> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| return ( | ||
| <div> | ||
| <h1>History</h1> | ||
|
|
||
| <div> | ||
| {items.map((item) => ( | ||
| <div key={item.id}> | ||
| <div> | ||
| <span>{item.method}</span> | ||
| <span>Status: {item.status}</span> | ||
| </div> | ||
|
|
||
| <p>{item.url}</p> | ||
|
|
||
| <div> | ||
| <span>Duration: {item.duration} ms</span> | ||
| <span>Request size: {formatBytes(item.request_size)}</span> | ||
| <span>Response size: {formatBytes(item.response_size)}</span> | ||
| <span>{new Date(item.timestamp).toLocaleString()}</span> | ||
| </div> | ||
|
|
||
| {item.error_details && <p>Error: {item.error_details}</p>} |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Верните локализацию текста и даты.
Маршрут использует [locale], но все новые подписи захардкожены на английском, а toLocaleString() использует локаль сервера, а не активную локаль маршрута. Передавайте локаль в компонент, используйте переводы и форматируйте дату через неё.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/pages/history-page/ui/history-page.tsx` around lines 18 - 45, Update the
history page component and its caller to receive the active route locale,
replace hardcoded labels with the existing translation mechanism, and pass that
locale to Date.toLocaleString for timestamp formatting. Preserve the current
empty-state, item rendering, and conditional error display behavior.
📋 Trello Task ID
⚡️ Summary
🛠 Type of change
feat(New feature)fix(Bug fix)refactor(Code improvement / Refactoring without changing logic)style(Formatting, CSS)docs(Documentation)chore(Configs, Build)test(Testing)Summary by CodeRabbit
Новые возможности
Исправления