chore: initial import to Nexus_Edu

This commit is contained in:
SpecialX
2025-11-28 19:23:19 +08:00
commit 38244630a7
153 changed files with 22541 additions and 0 deletions

89
src/lib/auth-context.tsx Normal file
View File

@@ -0,0 +1,89 @@
"use client";
import React, { createContext, useContext, useState, useEffect } from 'react';
import { UserProfileDto } from '../../UI_DTO';
import { authService, subscribeApiMode } from '@/services/api';
import { useRouter, usePathname } from 'next/navigation';
interface AuthContextType {
user: UserProfileDto | null;
loading: boolean;
login: (username: string, password: string) => Promise<void>;
logout: () => void;
register: (data: any) => Promise<void>;
}
const AuthContext = createContext<AuthContextType | undefined>(undefined);
export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [user, setUser] = useState<UserProfileDto | null>(null);
const [loading, setLoading] = useState(true);
const router = useRouter();
const pathname = usePathname() || '';
const checkAuth = async () => {
try {
const userData = await authService.me();
setUser(userData);
} catch (e) {
setUser(null);
if (!pathname.includes('/login')) {
router.push('/login');
}
} finally {
setLoading(false);
}
};
// Re-check auth when API mode changes (Strategy Pattern hook)
useEffect(() => {
return subscribeApiMode(() => {
setLoading(true);
checkAuth();
});
}, []);
useEffect(() => {
checkAuth();
}, []);
const login = async (username: string, password: string) => {
setLoading(true);
try {
const res = await authService.login(username, password);
setUser(res.user);
router.push('/dashboard');
} finally {
setLoading(false);
}
};
const register = async (data: any) => {
setLoading(true);
try {
const res = await authService.register(data);
setUser(res.user);
router.push('/dashboard');
} finally {
setLoading(false);
}
}
const logout = () => {
setUser(null);
localStorage.removeItem('token');
router.push('/login');
};
return (
<AuthContext.Provider value={{ user, loading, login, logout, register }}>
{children}
</AuthContext.Provider>
);
};
export const useAuth = () => {
const context = useContext(AuthContext);
if (!context) throw new Error('useAuth must be used within an AuthProvider');
return context;
};

35
src/lib/db.ts Normal file
View File

@@ -0,0 +1,35 @@
// This file would typically use mysql2/promise
// import mysql from 'mysql2/promise';
// Mock DB Configuration storage (In-memory for demo, use env vars in prod)
let dbConfig = {
host: 'localhost',
port: 3306,
user: 'root',
password: '',
database: 'edunexus'
};
// Mock Connection Pool
export const db = {
query: async (sql: string, params?: any[]) => {
// In a real app:
// const connection = await mysql.createConnection(dbConfig);
// const [rows] = await connection.execute(sql, params);
// return rows;
console.log(`[MockDB] Executing SQL: ${sql}`, params);
return [];
},
testConnection: async (config: typeof dbConfig) => {
// Simulate connection attempt
await new Promise(resolve => setTimeout(resolve, 1000));
if (config.host === 'error') throw new Error('Connection timed out');
// Update active config
dbConfig = config;
return true;
},
getConfig: () => dbConfig
};

24
src/lib/server-utils.ts Normal file
View File

@@ -0,0 +1,24 @@
import { NextResponse } from 'next/server';
// Simulate database latency
export const dbDelay = () => new Promise(resolve => setTimeout(resolve, 500));
// Standardize JSON success response
export function successResponse(data: any, status = 200) {
return NextResponse.json(data, { status });
}
// Standardize JSON error response
export function errorResponse(message: string, status = 400) {
return NextResponse.json({ success: false, message }, { status });
}
// Helper to extract token from header
export function extractToken(request: Request): string | null {
const authHeader = request.headers.get('authorization');
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return null;
}
return authHeader.split(' ')[1];
}