Archived
Admin for the menu and enquiries, plus gallery fixes
Admin - /api/admin: products CRUD, the enquiry inbox, and presigned photo upload straight to the bucket so images never pass through the app. Gated by platform.security.authenticated-paths = /api/admin/**, so any signed-in Authentik user is staff — the alternative is a role model a two-person bakery would never maintain. - /api/me is deliberately PUBLIC. The SPA asks on every page load, and requiring a login would bounce every anonymous visitor to Authentik just to read the menu. - /admin screens: product list with edit and remove, an editor with drag-free photo reordering and upload, and an enquiry inbox that flags anything the relay refused. Gallery - swipe on touch devices, which the react-awesome-slider it replaced had and this did not, plus arrow keys and position dots — with swipe there is otherwise nothing to say a card holds more than one photo. Vertical drags are ignored so page scrolling still works. - @BatchSize on the photo collection: the products page loaded the whole catalogue and Hibernate issued a query per product for its images, forty-odd round trips for a page that needs two. Three things the tests caught, none of which are obvious: - Adding the storage starter broke every existing test. It activates on a default endpoint, so an S3 client is built even in tests and dies on blank keys. - MockMvc's webAppContextSetup leaves the security filter chain OUT, so the first version of the security test passed 200s and proved the opposite of what it claimed. It needs .apply(springSecurity()). - Turning on the security starter turns on CSRF — for the PUBLIC contact form too, which then 403s. The SPA now reads the XSRF-TOKEN cookie and sends X-XSRF-TOKEN, and there is a test asserting the form is rejected without it.
This commit is contained in:
@@ -7,6 +7,9 @@ import ProductsPage from '@/pages/Products';
|
||||
import HistoryPage from '@/pages/History';
|
||||
import ContactPage from '@/pages/Contact';
|
||||
import NotFoundPage from '@/pages/NotFound';
|
||||
import AdminPage from '@/pages/admin/AdminPage';
|
||||
import ProductEditorPage from '@/pages/admin/ProductEditorPage';
|
||||
import { AuthProvider } from '@/lib/auth';
|
||||
|
||||
/**
|
||||
* Client-side navigation keeps the previous scroll position, which lands you halfway down a page you
|
||||
@@ -23,6 +26,7 @@ const ScrollToTop = () => {
|
||||
};
|
||||
|
||||
const App = () => (
|
||||
<AuthProvider>
|
||||
<div className="min-h-screen bg-bakery-50 flex flex-col">
|
||||
<ScrollToTop />
|
||||
<Header />
|
||||
@@ -32,11 +36,15 @@ const App = () => (
|
||||
<Route path="/products" element={<ProductsPage />} />
|
||||
<Route path="/history" element={<HistoryPage />} />
|
||||
<Route path="/contact" element={<ContactPage />} />
|
||||
<Route path="/admin" element={<AdminPage />} />
|
||||
<Route path="/admin/products/new" element={<ProductEditorPage />} />
|
||||
<Route path="/admin/products/:id" element={<ProductEditorPage />} />
|
||||
<Route path="*" element={<NotFoundPage />} />
|
||||
</Routes>
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
</AuthProvider>
|
||||
);
|
||||
|
||||
export default App;
|
||||
|
||||
Reference in New Issue
Block a user