feat: consolidate auth into NocoDB, add password reset, health check
Deploy / deploy (push) Failing after 9s
Deploy / deploy (push) Failing after 9s
- Migrate auth credentials from SQLite (auth.db) to NocoDB Users table with one-time migration function (auth.db → auth.db.bak) - Add email-based password reset via Cloudron SMTP (nodemailer) - Add GET /api/health endpoint for monitoring - Add startup env var validation with clear error messages - Strip sensitive fields (password_hash, reset_token) from all API responses - Add ForgotPassword + ResetPassword pages with i18n (en/ar) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+12
-1
@@ -3,12 +3,14 @@ import { useState, useEffect, createContext, lazy, Suspense } from 'react'
|
||||
import { AuthProvider, useAuth } from './contexts/AuthContext'
|
||||
import { LanguageProvider } from './i18n/LanguageContext'
|
||||
import { ToastProvider } from './components/ToastContainer'
|
||||
import { ThemeProvider } from './contexts/ThemeContext'
|
||||
import ErrorBoundary from './components/ErrorBoundary'
|
||||
import Layout from './components/Layout'
|
||||
import Tutorial from './components/Tutorial'
|
||||
import Modal from './components/Modal'
|
||||
import { api } from './utils/api'
|
||||
import { useLanguage } from './i18n/LanguageContext'
|
||||
import { useKeyboardShortcuts, DEFAULT_SHORTCUTS } from './hooks/useKeyboardShortcuts'
|
||||
|
||||
// Lazy-loaded page components
|
||||
const Dashboard = lazy(() => import('./pages/Dashboard'))
|
||||
@@ -32,6 +34,8 @@ const PublicReview = lazy(() => import('./pages/PublicReview'))
|
||||
const Issues = lazy(() => import('./pages/Issues'))
|
||||
const PublicIssueSubmit = lazy(() => import('./pages/PublicIssueSubmit'))
|
||||
const PublicIssueTracker = lazy(() => import('./pages/PublicIssueTracker'))
|
||||
const ForgotPassword = lazy(() => import('./pages/ForgotPassword'))
|
||||
const ResetPassword = lazy(() => import('./pages/ResetPassword'))
|
||||
|
||||
const TEAM_ROLES = [
|
||||
{ value: 'manager', label: 'Manager' },
|
||||
@@ -62,6 +66,9 @@ function AppContent() {
|
||||
const [profileForm, setProfileForm] = useState({ name: '', team_role: '', phone: '', brands: '' })
|
||||
const [profileSaving, setProfileSaving] = useState(false)
|
||||
|
||||
// Keyboard shortcuts
|
||||
useKeyboardShortcuts(DEFAULT_SHORTCUTS)
|
||||
|
||||
useEffect(() => {
|
||||
if (user && !authLoading) {
|
||||
loadInitialData()
|
||||
@@ -277,6 +284,8 @@ function AppContent() {
|
||||
<Suspense fallback={<div className="min-h-screen bg-surface-secondary flex items-center justify-center"><div className="animate-pulse text-text-tertiary">Loading...</div></div>}>
|
||||
<Routes>
|
||||
<Route path="/login" element={user ? <Navigate to="/" replace /> : <Login />} />
|
||||
<Route path="/forgot-password" element={user ? <Navigate to="/" replace /> : <ForgotPassword />} />
|
||||
<Route path="/reset-password" element={user ? <Navigate to="/" replace /> : <ResetPassword />} />
|
||||
<Route path="/review/:token" element={<PublicReview />} />
|
||||
<Route path="/submit-issue" element={<PublicIssueSubmit />} />
|
||||
<Route path="/track/:token" element={<PublicIssueTracker />} />
|
||||
@@ -320,7 +329,9 @@ function App() {
|
||||
<LanguageProvider>
|
||||
<AuthProvider>
|
||||
<ToastProvider>
|
||||
<AppContent />
|
||||
<ThemeProvider>
|
||||
<AppContent />
|
||||
</ThemeProvider>
|
||||
</ToastProvider>
|
||||
</AuthProvider>
|
||||
</LanguageProvider>
|
||||
|
||||
Reference in New Issue
Block a user