Add YAML-based store configs with bidirectional sync
Stores can now be defined as YAML files in the stores/ directory. On startup, YAML files are synced into the database. Changes made via the admin UI are written back to YAML files automatically. - Add store-sync service (load from files, export to files, write-back) - Add /api/stores/sync and /api/stores/export endpoints - Add Sync/Export buttons to admin UI - Mount stores/ volume in Docker - Include example store config template Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -42,6 +42,14 @@ export function deleteStore(id: number) {
|
||||
return api<void>(`/api/stores/${id}`, { method: 'DELETE' });
|
||||
}
|
||||
|
||||
export function exportStores() {
|
||||
return api<{ exported: number; directory: string }>('/api/stores/export', { method: 'POST' });
|
||||
}
|
||||
|
||||
export function syncStores() {
|
||||
return api<{ created: number; updated: number; errors: string[] }>('/api/stores/sync', { method: 'POST' });
|
||||
}
|
||||
|
||||
export function getCategories() {
|
||||
return api<any[]>('/api/categories');
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<script>
|
||||
import { getStores, toggleStore, deleteStore } from '$lib/api';
|
||||
import { getStores, toggleStore, deleteStore, exportStores, syncStores } from '$lib/api';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let stores = $state([]);
|
||||
let loading = $state(true);
|
||||
let syncMessage = $state('');
|
||||
|
||||
onMount(async () => {
|
||||
stores = await getStores();
|
||||
@@ -20,19 +21,56 @@
|
||||
await deleteStore(id);
|
||||
stores = stores.filter((s) => s.id !== id);
|
||||
}
|
||||
|
||||
async function handleSync() {
|
||||
syncMessage = '';
|
||||
const result = await syncStores();
|
||||
stores = await getStores();
|
||||
const parts = [];
|
||||
if (result.created) parts.push(`${result.created} created`);
|
||||
if (result.updated) parts.push(`${result.updated} updated`);
|
||||
if (result.errors.length) parts.push(`${result.errors.length} errors`);
|
||||
syncMessage = parts.length ? `Sync: ${parts.join(', ')}` : 'No changes found';
|
||||
if (result.errors.length) syncMessage += ' — ' + result.errors.join('; ');
|
||||
setTimeout(() => syncMessage = '', 5000);
|
||||
}
|
||||
|
||||
async function handleExport() {
|
||||
const result = await exportStores();
|
||||
syncMessage = `Exported ${result.exported} stores to ${result.directory}/`;
|
||||
setTimeout(() => syncMessage = '', 5000);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="max-w-7xl mx-auto px-4 py-6">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">Store Management</h1>
|
||||
<a
|
||||
href="/admin/stores/new"
|
||||
class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors"
|
||||
>
|
||||
Add Store
|
||||
</a>
|
||||
<div class="flex gap-2">
|
||||
<button onclick={handleSync}
|
||||
class="border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800 px-4 py-2 rounded-lg text-sm font-medium transition-colors"
|
||||
title="Reload store configs from YAML files in the stores/ directory">
|
||||
Sync from Files
|
||||
</button>
|
||||
<button onclick={handleExport}
|
||||
class="border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800 px-4 py-2 rounded-lg text-sm font-medium transition-colors"
|
||||
title="Export all store configs to YAML files">
|
||||
Export to Files
|
||||
</button>
|
||||
<a
|
||||
href="/admin/stores/new"
|
||||
class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors"
|
||||
>
|
||||
Add Store
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if syncMessage}
|
||||
<div class="mb-4 px-4 py-2.5 rounded-lg bg-blue-50 dark:bg-blue-900/20 text-blue-700 dark:text-blue-300 text-sm">
|
||||
{syncMessage}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if loading}
|
||||
<div class="animate-pulse space-y-3">
|
||||
{#each Array(5) as _}
|
||||
|
||||
Reference in New Issue
Block a user