All checks were successful
Build and Push Docker Images / docker (push) Successful in 2m15s
🎨 Complete UI redesign of admin panel with professional color scheme: ## New Modern Maritime Color Palette - Deep Sea Blue (#1D2939) - Sidebar background - Sail White (#F8F9FA) - Main background - Maritime Teal (#008B8B) - Primary accent - Sea Green (#1E8449) - Success/environmental theme - Muted Gold (#D68910) - Revenue highlights - Royal Purple (#884EA0) - Brand accent - Off-White (#EAECEF) - Text on dark backgrounds ## Admin Panel Features - ✅ JWT-based authentication system - ✅ Protected routes with middleware - ✅ Elegant sidebar navigation with Puffin logo - ✅ Dashboard with stat cards (Orders, CO₂, Revenue, Fulfillment) - ✅ Monaco harbor image background on login page - ✅ Responsive glassmorphism design - ✅ WCAG AA contrast compliance ## New Files - app/admin/ - Admin pages (login, dashboard, orders) - app/api/admin/ - Auth API routes (login, logout, verify) - components/admin/ - AdminSidebar component - lib/auth.ts - JWT authentication utilities - public/monaco_high_res.jpg - Luxury background image ## Updated - tailwind.config.js - Custom maritime color palette - package.json - Added jsonwebtoken dependency - app/layout.tsx - RootLayoutClient integration 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
77 lines
1.8 KiB
TypeScript
77 lines
1.8 KiB
TypeScript
import jwt from 'jsonwebtoken';
|
|
|
|
const JWT_SECRET = process.env.JWT_SECRET || 'fallback-secret-key';
|
|
const TOKEN_EXPIRY = '24h'; // Token expires in 24 hours
|
|
|
|
export interface AdminUser {
|
|
username: string;
|
|
isAdmin: true;
|
|
}
|
|
|
|
export interface JWTPayload extends AdminUser {
|
|
iat: number;
|
|
exp: number;
|
|
}
|
|
|
|
/**
|
|
* Verify admin credentials against environment variables
|
|
*/
|
|
export function verifyCredentials(username: string, password: string): boolean {
|
|
const adminUsername = process.env.ADMIN_USERNAME;
|
|
const adminPassword = process.env.ADMIN_PASSWORD;
|
|
|
|
if (!adminUsername || !adminPassword) {
|
|
console.error('Admin credentials not configured in environment variables');
|
|
return false;
|
|
}
|
|
|
|
return username === adminUsername && password === adminPassword;
|
|
}
|
|
|
|
/**
|
|
* Generate JWT token for authenticated admin
|
|
*/
|
|
export function generateToken(user: AdminUser): string {
|
|
return jwt.sign(user, JWT_SECRET, {
|
|
expiresIn: TOKEN_EXPIRY,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Verify and decode JWT token
|
|
* Returns the decoded payload or null if invalid
|
|
*/
|
|
export function verifyToken(token: string): JWTPayload | null {
|
|
try {
|
|
const decoded = jwt.verify(token, JWT_SECRET) as JWTPayload;
|
|
return decoded;
|
|
} catch (error) {
|
|
console.error('JWT verification failed:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extract token from Authorization header
|
|
* Supports both "Bearer token" and plain token formats
|
|
*/
|
|
export function extractToken(authHeader: string | null): string | null {
|
|
if (!authHeader) return null;
|
|
|
|
if (authHeader.startsWith('Bearer ')) {
|
|
return authHeader.substring(7);
|
|
}
|
|
|
|
return authHeader;
|
|
}
|
|
|
|
/**
|
|
* Check if user is authenticated admin
|
|
*/
|
|
export function isAuthenticated(token: string | null): boolean {
|
|
if (!token) return false;
|
|
|
|
const payload = verifyToken(token);
|
|
return payload !== null && payload.isAdmin === true;
|
|
}
|