logs
Some checks failed
Backend CI / test (push) Waiting to run
Backend CI / build (push) Blocked by required conditions
Deploy Mnemo Cards / Deploy Backend (push) Waiting to run
Deploy Mnemo Cards / Deploy Web App (push) Blocked by required conditions
Deploy Mnemo Cards / Final Verification (push) Blocked by required conditions
Deploy Admin Panel / Deploy Admin Panel (push) Has been cancelled
Deploy Admin Panel / Admin Panel Verification (push) Has been cancelled
Some checks failed
Backend CI / test (push) Waiting to run
Backend CI / build (push) Blocked by required conditions
Deploy Mnemo Cards / Deploy Backend (push) Waiting to run
Deploy Mnemo Cards / Deploy Web App (push) Blocked by required conditions
Deploy Mnemo Cards / Final Verification (push) Blocked by required conditions
Deploy Admin Panel / Deploy Admin Panel (push) Has been cancelled
Deploy Admin Panel / Admin Panel Verification (push) Has been cancelled
This commit is contained in:
parent
215854e461
commit
221ae2a049
5 changed files with 53 additions and 42 deletions
|
|
@ -4,21 +4,30 @@ import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContai
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
import { analyticsApi, type DashboardData, type ChartDataPoint } from '@/api/analytics'
|
import { analyticsApi, type DashboardData, type ChartDataPoint } from '@/api/analytics'
|
||||||
|
import { useAuthStore } from '@/stores/authStore'
|
||||||
|
|
||||||
export default function DashboardPage() {
|
export default function DashboardPage() {
|
||||||
|
const { isAuthenticated, token } = useAuthStore()
|
||||||
|
|
||||||
|
// Only make requests if authenticated and token exists
|
||||||
|
const isReady = isAuthenticated && !!token && !!localStorage.getItem('admin_token')
|
||||||
|
|
||||||
const { data: dashboardData, isLoading: dashboardLoading } = useQuery<DashboardData>({
|
const { data: dashboardData, isLoading: dashboardLoading } = useQuery<DashboardData>({
|
||||||
queryKey: ['dashboard'],
|
queryKey: ['dashboard'],
|
||||||
queryFn: analyticsApi.getDashboard,
|
queryFn: analyticsApi.getDashboard,
|
||||||
|
enabled: isReady,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { data: usersChartData } = useQuery<{ data: ChartDataPoint[] }>({
|
const { data: usersChartData } = useQuery<{ data: ChartDataPoint[] }>({
|
||||||
queryKey: ['users-chart'],
|
queryKey: ['users-chart'],
|
||||||
queryFn: analyticsApi.getUsersChart,
|
queryFn: analyticsApi.getUsersChart,
|
||||||
|
enabled: isReady,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { data: revenueChartData } = useQuery<{ data: ChartDataPoint[] }>({
|
const { data: revenueChartData } = useQuery<{ data: ChartDataPoint[] }>({
|
||||||
queryKey: ['revenue-chart'],
|
queryKey: ['revenue-chart'],
|
||||||
queryFn: analyticsApi.getRevenueChart,
|
queryFn: analyticsApi.getRevenueChart,
|
||||||
|
enabled: isReady,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (dashboardLoading) {
|
if (dashboardLoading) {
|
||||||
|
|
|
||||||
|
|
@ -94,9 +94,28 @@ export default function LoginPage() {
|
||||||
setIsVerifying(false)
|
setIsVerifying(false)
|
||||||
|
|
||||||
console.log('Login successful, setting token and user')
|
console.log('Login successful, setting token and user')
|
||||||
|
console.log('Token received:', data.token ? `Token exists, length: ${data.token.length}` : 'NO TOKEN!')
|
||||||
|
console.log('User received:', data.user)
|
||||||
|
|
||||||
|
// Save token first
|
||||||
login(data.token, data.user)
|
login(data.token, data.user)
|
||||||
|
|
||||||
|
// Verify token was saved
|
||||||
|
const savedToken = localStorage.getItem('admin_token')
|
||||||
|
console.log('Token saved verification:', savedToken ? `Token exists, length: ${savedToken.length}` : 'Token NOT saved!')
|
||||||
|
|
||||||
|
if (!savedToken) {
|
||||||
|
console.error('CRITICAL: Token was not saved to localStorage!')
|
||||||
|
toast.error('Failed to save authentication token')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
toast.success('Welcome back!')
|
toast.success('Welcome back!')
|
||||||
navigate('/')
|
|
||||||
|
// Small delay to ensure token is available for subsequent requests
|
||||||
|
setTimeout(() => {
|
||||||
|
navigate('/')
|
||||||
|
}, 100)
|
||||||
},
|
},
|
||||||
onError: (error: unknown) => {
|
onError: (error: unknown) => {
|
||||||
console.error('Verify code error:', error)
|
console.error('Verify code error:', error)
|
||||||
|
|
|
||||||
|
|
@ -159,9 +159,7 @@ class AdminAuthApiV2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate JWT token using JwtService (same as regular auth)
|
// Generate JWT token using JwtService (same as regular auth)
|
||||||
print('[ADMIN AUTH] Generating tokens for admin user ${user.id}');
|
|
||||||
final tokens = await _jwtService.generateTokens(user);
|
final tokens = await _jwtService.generateTokens(user);
|
||||||
print('[ADMIN AUTH] Generated access token, length: ${tokens.accessToken.length}');
|
|
||||||
|
|
||||||
return _json({
|
return _json({
|
||||||
'success': true,
|
'success': true,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:mnemo_cards_backend/api/v2/jwt_service.dart';
|
import 'package:mnemo_cards_backend/api/v2/jwt_service.dart';
|
||||||
import 'package:mnemo_cards_backend/user/user_manager.dart';
|
import 'package:mnemo_cards_backend/user/user_manager.dart';
|
||||||
import 'package:shelf/shelf.dart';
|
import 'package:shelf/shelf.dart';
|
||||||
|
|
@ -89,12 +91,14 @@ Middleware authorizeV2(UserManager userManager, JwtService jwtService) {
|
||||||
// Shelf normalizes headers to lowercase, so we check 'authorization'
|
// Shelf normalizes headers to lowercase, so we check 'authorization'
|
||||||
final authHeader = request.headers['authorization'];
|
final authHeader = request.headers['authorization'];
|
||||||
|
|
||||||
// Debug logging for admin endpoints
|
// Debug logging for admin endpoints - use stderr to ensure it's visible
|
||||||
if (normalizedPath.startsWith('/admin/')) {
|
if (normalizedPath.startsWith('/admin/')) {
|
||||||
print('[AUTH DEBUG] Path: $normalizedPath');
|
stderr.writeln('[AUTH DEBUG] Path: $normalizedPath');
|
||||||
print('[AUTH DEBUG] All headers: ${request.headers}');
|
stderr.writeln('[AUTH DEBUG] Authorization header present: ${authHeader != null}');
|
||||||
print('[AUTH DEBUG] Authorization header: $authHeader');
|
if (authHeader != null) {
|
||||||
print('[AUTH DEBUG] Authorization header type: ${authHeader.runtimeType}');
|
stderr.writeln('[AUTH DEBUG] Authorization header length: ${authHeader.length}');
|
||||||
|
stderr.writeln('[AUTH DEBUG] Authorization header starts with Bearer: ${authHeader.startsWith('Bearer ')}');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final hasBearerToken =
|
final hasBearerToken =
|
||||||
|
|
@ -113,11 +117,11 @@ Middleware authorizeV2(UserManager userManager, JwtService jwtService) {
|
||||||
|
|
||||||
final token = authHeader.substring(7); // Remove "Bearer " prefix
|
final token = authHeader.substring(7); // Remove "Bearer " prefix
|
||||||
|
|
||||||
// Debug logging for admin endpoints
|
// Debug logging for admin endpoints - use stderr
|
||||||
if (normalizedPath.startsWith('/admin/')) {
|
if (normalizedPath.startsWith('/admin/')) {
|
||||||
print('[AUTH DEBUG] Verifying token for path: $normalizedPath');
|
stderr.writeln('[AUTH DEBUG] Verifying token for path: $normalizedPath');
|
||||||
print('[AUTH DEBUG] Token length: ${token.length}');
|
stderr.writeln('[AUTH DEBUG] Token length: ${token.length}');
|
||||||
print('[AUTH DEBUG] Token preview: ${token.length > 50 ? token.substring(0, 50) + "..." : token}');
|
stderr.writeln('[AUTH DEBUG] Token preview: ${token.length > 50 ? token.substring(0, 50) + "..." : token}');
|
||||||
}
|
}
|
||||||
|
|
||||||
final userId = jwtService.verifyAccessToken(token);
|
final userId = jwtService.verifyAccessToken(token);
|
||||||
|
|
@ -125,13 +129,13 @@ Middleware authorizeV2(UserManager userManager, JwtService jwtService) {
|
||||||
if (userId == null) {
|
if (userId == null) {
|
||||||
// Additional debug info for admin endpoints
|
// Additional debug info for admin endpoints
|
||||||
if (normalizedPath.startsWith('/admin/')) {
|
if (normalizedPath.startsWith('/admin/')) {
|
||||||
print('[AUTH DEBUG] Token verification failed for path: $normalizedPath');
|
stderr.writeln('[AUTH DEBUG] Token verification failed for path: $normalizedPath');
|
||||||
// Try to extract payload to see what's wrong
|
// Try to extract payload to see what's wrong
|
||||||
try {
|
try {
|
||||||
final payload = jwtService.extractPayload(token);
|
final payload = jwtService.extractPayload(token);
|
||||||
print('[AUTH DEBUG] Extracted payload: $payload');
|
stderr.writeln('[AUTH DEBUG] Extracted payload: $payload');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('[AUTH DEBUG] Could not extract payload: $e');
|
stderr.writeln('[AUTH DEBUG] Could not extract payload: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Response(
|
return Response(
|
||||||
|
|
@ -141,6 +145,10 @@ Middleware authorizeV2(UserManager userManager, JwtService jwtService) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (normalizedPath.startsWith('/admin/')) {
|
||||||
|
stderr.writeln('[AUTH DEBUG] Token verified successfully, userId: $userId');
|
||||||
|
}
|
||||||
|
|
||||||
// Get user from database
|
// Get user from database
|
||||||
final userIdInt = int.tryParse(userId);
|
final userIdInt = int.tryParse(userId);
|
||||||
if (userIdInt == null) {
|
if (userIdInt == null) {
|
||||||
|
|
|
||||||
|
|
@ -47,10 +47,6 @@ class JwtService {
|
||||||
final accessToken = _createSimpleJwt(accessTokenPayload);
|
final accessToken = _createSimpleJwt(accessTokenPayload);
|
||||||
final refreshToken = _createSimpleJwt(refreshTokenPayload);
|
final refreshToken = _createSimpleJwt(refreshTokenPayload);
|
||||||
|
|
||||||
print('[JWT DEBUG] Generated access token for user ${user.id}');
|
|
||||||
print('[JWT DEBUG] Access token payload: $accessTokenPayload');
|
|
||||||
print('[JWT DEBUG] Access token length: ${accessToken.length}');
|
|
||||||
|
|
||||||
// Store refresh token for invalidation
|
// Store refresh token for invalidation
|
||||||
final userId = user.id;
|
final userId = user.id;
|
||||||
if (userId == null) {
|
if (userId == null) {
|
||||||
|
|
@ -71,37 +67,25 @@ class JwtService {
|
||||||
try {
|
try {
|
||||||
final payload = _verifySimpleJwt(token);
|
final payload = _verifySimpleJwt(token);
|
||||||
if (payload == null) {
|
if (payload == null) {
|
||||||
print('[JWT DEBUG] Token verification failed: payload is null');
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
print('[JWT DEBUG] Token payload: $payload');
|
|
||||||
|
|
||||||
// Check if token is expired
|
// Check if token is expired
|
||||||
final exp = payload['exp'] as int?;
|
final exp = payload['exp'] as int?;
|
||||||
if (exp != null) {
|
if (exp != null) {
|
||||||
final expiryTime = DateTime.fromMillisecondsSinceEpoch(exp * 1000);
|
final expiryTime = DateTime.fromMillisecondsSinceEpoch(exp * 1000);
|
||||||
final now = DateTime.now();
|
if (DateTime.now().isAfter(expiryTime)) {
|
||||||
print('[JWT DEBUG] Token expiry: $expiryTime, now: $now, expired: ${now.isAfter(expiryTime)}');
|
|
||||||
if (now.isAfter(expiryTime)) {
|
|
||||||
print('[JWT DEBUG] Token is expired');
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check token type
|
// Check token type
|
||||||
final tokenType = payload['type'];
|
if (payload['type'] != 'access') {
|
||||||
print('[JWT DEBUG] Token type: $tokenType');
|
|
||||||
if (tokenType != 'access') {
|
|
||||||
print('[JWT DEBUG] Token type mismatch: expected "access", got "$tokenType"');
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final userId = payload['userId']?.toString();
|
return payload['userId']?.toString();
|
||||||
print('[JWT DEBUG] Extracted userId: $userId');
|
|
||||||
return userId;
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('[JWT DEBUG] Token verification exception: $e');
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -162,7 +146,6 @@ class JwtService {
|
||||||
try {
|
try {
|
||||||
final parts = token.split('.');
|
final parts = token.split('.');
|
||||||
if (parts.length != 3) {
|
if (parts.length != 3) {
|
||||||
print('[JWT DEBUG] Invalid token format: expected 3 parts, got ${parts.length}');
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -177,19 +160,13 @@ class JwtService {
|
||||||
final expectedSignatureB64 = base64UrlEncode(expectedSignature);
|
final expectedSignatureB64 = base64UrlEncode(expectedSignature);
|
||||||
|
|
||||||
if (signatureB64 != expectedSignatureB64) {
|
if (signatureB64 != expectedSignatureB64) {
|
||||||
print('[JWT DEBUG] Invalid signature');
|
|
||||||
print('[JWT DEBUG] Expected: $expectedSignatureB64');
|
|
||||||
print('[JWT DEBUG] Got: $signatureB64');
|
|
||||||
return null; // Invalid signature
|
return null; // Invalid signature
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode payload
|
// Decode payload
|
||||||
final payloadJson = utf8.decode(base64Url.decode(payloadB64));
|
final payloadJson = utf8.decode(base64Url.decode(payloadB64));
|
||||||
final payload = jsonDecode(payloadJson) as Map<String, dynamic>;
|
return jsonDecode(payloadJson) as Map<String, dynamic>;
|
||||||
print('[JWT DEBUG] Decoded payload: $payload');
|
|
||||||
return payload;
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('[JWT DEBUG] Exception during token verification: $e');
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue