Skip to content

Commit 44ba764

Browse files
Merge pull request #313 from DevLoversTeam/fix/dev-oauth-dashboard-login-redirect
fix(proxy): handle missing locale correctly for /dashboard redirects
2 parents 72e54d5 + 954ebf2 commit 44ba764

2 files changed

Lines changed: 87 additions & 47 deletions

File tree

frontend/.env.example

Lines changed: 77 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,105 @@
1+
# --- Core / Environment
2+
APP_ADDITIONAL_ORIGINS=https://admin.example.test
3+
APP_ENV=
4+
APP_ORIGIN=https://example.test
5+
APP_URL=
6+
NEXT_PUBLIC_SITE_URL=
7+
8+
# --- Database
19
DATABASE_URL=
10+
11+
# --- Upstash Redis (REST)
12+
UPSTASH_REDIS_REST_URL=
13+
UPSTASH_REDIS_REST_TOKEN=
14+
15+
# --- Auth (app)
216
AUTH_SECRET=
317

4-
CLOUDINARY_CLOUD_NAME=
18+
# --- OAuth: Google
19+
GOOGLE_CLIENT_ID_DEVELOP=
20+
GOOGLE_CLIENT_ID_LOCAL=
21+
GOOGLE_CLIENT_ID_PROD=
22+
GOOGLE_CLIENT_REDIRECT_URI_DEVELOP=
23+
GOOGLE_CLIENT_REDIRECT_URI_LOCAL=
24+
GOOGLE_CLIENT_REDIRECT_URI_PROD=
25+
GOOGLE_CLIENT_SECRET_DEVELOP=
26+
GOOGLE_CLIENT_SECRET_LOCAL=
27+
GOOGLE_CLIENT_SECRET_PROD=
28+
29+
# --- OAuth: GitHub
30+
GITHUB_CLIENT_ID_DEVELOP=
31+
GITHUB_CLIENT_ID_LOCAL=
32+
GITHUB_CLIENT_ID_PROD=
33+
GITHUB_CLIENT_REDIRECT_URI_DEVELOP=
34+
GITHUB_CLIENT_REDIRECT_URI_LOCAL=
35+
GITHUB_CLIENT_REDIRECT_URI_PROD=
36+
GITHUB_CLIENT_SECRET_DEVELOP=
37+
GITHUB_CLIENT_SECRET_LOCAL=
38+
GITHUB_CLIENT_SECRET_PROD=
39+
40+
# --- Cloudinary
541
CLOUDINARY_API_KEY=
642
CLOUDINARY_API_SECRET=
43+
CLOUDINARY_CLOUD_NAME=
744
CLOUDINARY_UPLOAD_FOLDER=
8-
945
CLOUDINARY_URL=
10-
ENABLE_ADMIN_API=
1146

47+
# --- Payments (Stripe)
48+
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=
1249
PAYMENTS_ENABLED=
13-
NEXT_PUBLIC_PAYMENTS_ENABLED=
50+
# Options: test, live (defaults to test in development, live in production)
51+
STRIPE_MODE=
1452
STRIPE_SECRET_KEY=
1553
STRIPE_WEBHOOK_SECRET=
1654

17-
STRIPE_MODE=
18-
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=
55+
# --- Admin / Internal ops
56+
ENABLE_ADMIN_API=
57+
INTERNAL_JANITOR_MIN_INTERVAL_SECONDS=
58+
INTERNAL_JANITOR_SECRET=
59+
JANITOR_URL=
1960

20-
NEXT_PUBLIC_SITE_URL=
21-
NEXT_PUBLIC_SITE_URL=
61+
# --- Quiz
62+
QUIZ_ENCRYPTION_KEY=
2263

64+
# --- Telegram
2365
TELEGRAM_BOT_TOKEN=
2466
TELEGRAM_CHAT_ID=
2567

26-
GOOGLE_CLIENT_ID=
27-
GOOGLE_CLIENT_SECRET=
28-
GOOGLE_CLIENT_REDIRECT_URI_LOCAL=
29-
GOOGLE_CLIENT_REDIRECT_URI_DEVELOP=
30-
GOOGLE_CLIENT_REDIRECT_URI_PROD=
31-
GITHUB_CLIENT_ID_DEVELOP=
32-
GITHUB_CLIENT_SECRET_DEVELOP=
33-
GITHUB_CLIENT_REDIRECT_URI_DEVELOP=
34-
35-
36-
APP_ENV=
37-
38-
INTERNAL_JANITOR_SECRET=
39-
INTERNAL_JANITOR_MIN_INTERVAL_SECONDS=
40-
41-
QUIZ_ENCRYPTION_KEY=
42-
43-
GMAIL_USER=
44-
GMAIL_APP_PASSWORD=
68+
# --- Email (Gmail SMTP)
4569
EMAIL_FROM=
46-
APP_URL=
70+
GMAIL_APP_PASSWORD=
71+
GMAIL_USER=
4772

73+
# --- Security
4874
CSRF_SECRET=
4975

50-
CHECKOUT_RATE_LIMIT_MAX=
51-
CHECKOUT_RATE_LIMIT_WINDOW_SECONDS=
76+
CHECKOUT_RATE_LIMIT_MAX=10
77+
CHECKOUT_RATE_LIMIT_WINDOW_SECONDS=300
5278

53-
STRIPE_WEBHOOK_INVALID_SIG_RL_MAX=
54-
STRIPE_WEBHOOK_INVALID_SIG_RL_WINDOW_SECONDS=
79+
# Stripe webhook rate limit envs (applied per reason; reason-specific overrides generic).
80+
# Missing signature has its own envs with fallback to generic, then legacy invalid_sig.
81+
STRIPE_WEBHOOK_MISSING_SIG_RL_MAX=30
82+
STRIPE_WEBHOOK_MISSING_SIG_RL_WINDOW_SECONDS=60
5583

56-
RATE_LIMIT_DISABLED=
84+
# Generic Stripe webhook rate limit fallback (applies to missing_sig and invalid_sig).
85+
STRIPE_WEBHOOK_RL_MAX=30
86+
STRIPE_WEBHOOK_RL_WINDOW_SECONDS=60
5787

58-
STRIPE_WEBHOOK_MISSING_SIG_RL_MAX=
59-
STRIPE_WEBHOOK_MISSING_SIG_RL_WINDOW_SECONDS=
88+
# Invalid signature envs (canonical for invalid_sig, legacy fallback for missing_sig).
89+
STRIPE_WEBHOOK_INVALID_SIG_RL_MAX=30
90+
STRIPE_WEBHOOK_INVALID_SIG_RL_WINDOW_SECONDS=60
6091

61-
STRIPE_WEBHOOK_RL_MAX=
62-
STRIPE_WEBHOOK_RL_WINDOW_SECONDS=
92+
# SECURITY: If true, trust Cloudflare's cf-connecting-ip header for rate limiting.
93+
# Enable ONLY when traffic is fronted by Cloudflare (header is set by Cloudflare at the edge).
94+
# Default: false (0). Keep 0 in untrusted environments to avoid IP spoofing.
95+
TRUST_CF_CONNECTING_IP=0
6396

64-
APP_ORIGIN=
65-
APP_ADDITIONAL_ORIGINS=
97+
# SECURITY: If true, trust x-real-ip / x-forwarded-for headers for rate limiting.
98+
# Enable ONLY behind Cloudflare or a trusted reverse proxy that overwrites these headers.
99+
# Default: false (empty/0/false).
100+
TRUST_FORWARDED_HEADERS=0
66101

67-
TRUST_CF_CONNECTING_IP=
68-
TRUST_FORWARDED_HEADERS=
102+
# emergency switch
103+
RATE_LIMIT_DISABLED=0
69104

70-
GROQ_API_KEY=
71-
72-
UPSTASH_REDIS_REST_URL=
73-
UPSTASH_REDIS_REST_TOKEN=
105+
GROQ_API_KEY=

frontend/proxy.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { NextRequest, NextResponse } from 'next/server';
22
import createIntlMiddleware from 'next-intl/middleware';
33

4+
import { defaultLocale, locales } from '@/i18n/config';
45
import { AuthTokenPayload } from '@/lib/auth';
56

67
import { routing } from './i18n/routing';
@@ -52,6 +53,13 @@ function isAuthenticated(req: NextRequest): boolean {
5253

5354
const intlMiddleware = createIntlMiddleware(routing);
5455

56+
function resolveLocaleFromPathname(pathname: string): string {
57+
const maybeLocale = pathname.split('/')[1];
58+
return locales.includes(maybeLocale as (typeof locales)[number])
59+
? maybeLocale
60+
: defaultLocale;
61+
}
62+
5563
function authMiddleware(req: NextRequest) {
5664
const { pathname } = req.nextUrl;
5765
const authenticated = isAuthenticated(req);
@@ -61,7 +69,7 @@ function authMiddleware(req: NextRequest) {
6169

6270
if (pathnameWithoutLocale.startsWith('/dashboard')) {
6371
if (!authenticated) {
64-
const locale = pathname.split('/')[1] || 'en';
72+
const locale = resolveLocaleFromPathname(pathname);
6573
return NextResponse.redirect(new URL(`/${locale}/login`, req.url));
6674
}
6775
}
@@ -81,7 +89,7 @@ export function proxy(req: NextRequest) {
8189
return NextResponse.redirect(new URL('/en', req.url));
8290
}
8391

84-
const locale = req.nextUrl.pathname.split('/')[1] || 'en';
92+
const locale = resolveLocaleFromPathname(req.nextUrl.pathname);
8593

8694
const authResponse = authMiddleware(req);
8795
if (authResponse) return authResponse;

0 commit comments

Comments
 (0)