feat: isolate catalog data per primary user (accountId)

- Add accountId to Farmer, MedicationCatalogItem, PathogenCatalogItem, AntibioticCatalogItem
- Create new /api/catalog endpoints for CUSTOMER role only
- Remove DemoDataInitializer (no more demo data generation)
- Add DefaultUserInitializer for admin user creation only
- Update repositories with accountId-based query methods
- Update CatalogService with accountId isolation and role-based access
- Update SecurityConfig: /api/catalog/** for CUSTOMER, /api/admin/** for ADMIN
- Update frontend AdministrationPage to use new /api/catalog endpoints
- Migrate existing data without accountId to first admin user
This commit is contained in:
2026-03-18 20:40:10 +01:00
parent 09e6d07c2d
commit f9b83a166d
18 changed files with 256 additions and 123 deletions

View File

@@ -6,7 +6,7 @@
* - MINOR: New functionality (backward compatible)
* - PATCH: Bug fixes (backward compatible)
*/
export const APP_VERSION = "0.8.0";
export const APP_VERSION = "0.9.2";
/**
* Build date - set at build time

View File

@@ -122,7 +122,7 @@ export default function AdministrationPage() {
useEffect(() => {
async function load() {
try {
const response = await apiGet<AdministrationOverview>("/admin");
const response = await apiGet<AdministrationOverview>("/catalog/overview");
setDatasets(normalizeOverview(response));
} catch (loadError) {
setMessage((loadError as Error).message);
@@ -176,7 +176,7 @@ export default function AdministrationPage() {
let response: EditableRow[];
switch (selectedDataset) {
case "farmers":
response = await apiPost<EditableRow[]>("/admin/farmers", rows.map((row) => ({
response = await apiPost<EditableRow[]>("/catalog/farmers", rows.map((row) => ({
id: row.id || null,
name: row.name,
email: row.email || null,
@@ -184,7 +184,7 @@ export default function AdministrationPage() {
})));
break;
case "medications":
response = await apiPost<EditableRow[]>("/admin/medications", rows.map((row) => ({
response = await apiPost<EditableRow[]>("/catalog/medications", rows.map((row) => ({
id: row.id || null,
name: row.name,
category: row.category,
@@ -192,7 +192,7 @@ export default function AdministrationPage() {
})));
break;
case "pathogens":
response = await apiPost<EditableRow[]>("/admin/pathogens", rows.map((row) => ({
response = await apiPost<EditableRow[]>("/catalog/pathogens", rows.map((row) => ({
id: row.id || null,
code: row.code || null,
name: row.name,
@@ -201,7 +201,7 @@ export default function AdministrationPage() {
})));
break;
case "antibiotics":
response = await apiPost<EditableRow[]>("/admin/antibiotics", rows.map((row) => ({
response = await apiPost<EditableRow[]>("/catalog/antibiotics", rows.map((row) => ({
id: row.id || null,
code: row.code || null,
name: row.name,