Some checks failed
Basil CI/CD Pipeline / Code Linting (push) Successful in 3m18s
Basil CI/CD Pipeline / Web Tests (push) Successful in 3m31s
Basil CI/CD Pipeline / Security Scanning (push) Has been cancelled
Basil CI/CD Pipeline / API Tests (push) Failing after 3m56s
Basil CI/CD Pipeline / Shared Package Tests (push) Successful in 3m11s
Basil CI/CD Pipeline / Trigger Deployment (push) Has been cancelled
Basil CI/CD Pipeline / Build All Packages (push) Has been cancelled
Basil CI/CD Pipeline / E2E Tests (push) Has been cancelled
Basil CI/CD Pipeline / Build & Push Docker Images (push) Has been cancelled
Introduces Family as the tenant boundary so recipes and cookbooks can be scoped per household instead of every user seeing everything. Adds a centralized access filter, an invite/membership UI, a first-login prompt to create a family, and locks down the previously unauthenticated backup routes to admin only. - Family and FamilyMember models with OWNER/MEMBER roles; familyId on Recipe and Cookbook (ON DELETE SET NULL so deleting a family orphans content rather than destroying it). - access.service.ts composes a single WhereInput covering owner, family, PUBLIC visibility, and direct share; admins short-circuit to full access. - recipes/cookbooks routes now require auth, strip client-supplied userId/familyId on create, and gate mutations with canMutate checks. Auto-filter helpers scoped to the same family to prevent cross-tenant leakage via shared tag names. - families.routes.ts exposes list/create/get/rename/delete plus add/remove member, with last-owner protection on removal. - FamilyGate component blocks the authenticated UI with a modal if the user has zero memberships, prompting them to create their first family; Family page provides ongoing management. - backup.routes.ts now requires admin; it had no auth at all before. - Bumps version to 2026.04.008 and documents the monotonic PPP counter in CLAUDE.md. Migration SQL is generated locally but not tracked (per existing .gitignore); apply 20260416010000_add_family_tenant to prod during deploy. Run backfill-family-tenant.ts once post-migration to assign existing content to a default owner's family. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
89 lines
3.8 KiB
TypeScript
89 lines
3.8 KiB
TypeScript
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
|
|
import { AuthProvider } from './contexts/AuthContext';
|
|
import { ThemeProvider } from './contexts/ThemeContext';
|
|
import ProtectedRoute from './components/ProtectedRoute';
|
|
import UserMenu from './components/UserMenu';
|
|
import ThemeToggle from './components/ThemeToggle';
|
|
import FamilyGate from './components/FamilyGate';
|
|
import Login from './pages/Login';
|
|
import Register from './pages/Register';
|
|
import AuthCallback from './pages/AuthCallback';
|
|
import Cookbooks from './pages/Cookbooks';
|
|
import CookbookDetail from './pages/CookbookDetail';
|
|
import EditCookbook from './pages/EditCookbook';
|
|
import RecipeList from './pages/RecipeList';
|
|
import RecipeDetail from './pages/RecipeDetail';
|
|
import RecipeImport from './pages/RecipeImport';
|
|
import NewRecipe from './pages/NewRecipe';
|
|
import UnifiedEditRecipe from './pages/UnifiedEditRecipe';
|
|
import CookingMode from './pages/CookingMode';
|
|
import Family from './pages/Family';
|
|
import { APP_VERSION } from './version';
|
|
import './App.css';
|
|
|
|
function App() {
|
|
return (
|
|
<Router>
|
|
<ThemeProvider>
|
|
<AuthProvider>
|
|
<FamilyGate>
|
|
<div className="app">
|
|
<header className="header">
|
|
<div className="container">
|
|
<div className="logo-container">
|
|
<h1 className="logo">
|
|
<Link to="/" title={`Basil v${APP_VERSION}`}>🌿 Basil</Link>
|
|
</h1>
|
|
<span className="version" title={`Version ${APP_VERSION}`}>v{APP_VERSION}</span>
|
|
</div>
|
|
<nav>
|
|
<Link to="/">Cookbooks</Link>
|
|
<Link to="/recipes">All Recipes</Link>
|
|
<Link to="/recipes/new">New Recipe</Link>
|
|
<Link to="/recipes/import">Import Recipe</Link>
|
|
</nav>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
|
|
<ThemeToggle />
|
|
<UserMenu />
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<main className="main">
|
|
<div className="container">
|
|
<Routes>
|
|
{/* Public Routes */}
|
|
<Route path="/login" element={<Login />} />
|
|
<Route path="/register" element={<Register />} />
|
|
<Route path="/auth/callback" element={<AuthCallback />} />
|
|
|
|
{/* Protected Routes */}
|
|
<Route path="/" element={<ProtectedRoute><Cookbooks /></ProtectedRoute>} />
|
|
<Route path="/cookbooks/:id" element={<ProtectedRoute><CookbookDetail /></ProtectedRoute>} />
|
|
<Route path="/cookbooks/:id/edit" element={<ProtectedRoute><EditCookbook /></ProtectedRoute>} />
|
|
<Route path="/recipes" element={<ProtectedRoute><RecipeList /></ProtectedRoute>} />
|
|
<Route path="/recipes/:id" element={<ProtectedRoute><RecipeDetail /></ProtectedRoute>} />
|
|
<Route path="/recipes/:id/edit" element={<ProtectedRoute><UnifiedEditRecipe /></ProtectedRoute>} />
|
|
<Route path="/recipes/:id/cook" element={<ProtectedRoute><CookingMode /></ProtectedRoute>} />
|
|
<Route path="/recipes/new" element={<ProtectedRoute><NewRecipe /></ProtectedRoute>} />
|
|
<Route path="/recipes/import" element={<ProtectedRoute><RecipeImport /></ProtectedRoute>} />
|
|
<Route path="/family" element={<ProtectedRoute><Family /></ProtectedRoute>} />
|
|
</Routes>
|
|
</div>
|
|
</main>
|
|
|
|
<footer className="footer">
|
|
<div className="container">
|
|
<p>Basil - Your Recipe Manager</p>
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
</FamilyGate>
|
|
</AuthProvider>
|
|
</ThemeProvider>
|
|
</Router>
|
|
);
|
|
}
|
|
|
|
export default App;
|