feat(settings): add database seeder section with one-time execution and lock mechanism
deploy-chabokan / deploy (push) Canceled after 0s
deploy-chabokan / deploy (push) Canceled after 0s
This commit is contained in:
@@ -97,5 +97,45 @@ export const SettingService = {
|
||||
console.error('Error bulk updating search settings:', err);
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get database seeding status
|
||||
* @returns {Promise<{isSeeded: boolean, seededAt: string|null, seededBy: string|null}>}
|
||||
*/
|
||||
getSeedStatus: async () => {
|
||||
const config = {
|
||||
method: 'get',
|
||||
url: '/api/settings/seed-status',
|
||||
withCredentials: true,
|
||||
headers: SettingService._getAuthHeader()
|
||||
};
|
||||
try {
|
||||
const response = await axios(config);
|
||||
return response.data.data;
|
||||
} catch (err) {
|
||||
console.error('Error fetching seed status:', err);
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Execute one-time database seeding
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
seedDatabase: async () => {
|
||||
const config = {
|
||||
method: 'post',
|
||||
url: '/api/settings/seed-database',
|
||||
withCredentials: true,
|
||||
headers: SettingService._getAuthHeader()
|
||||
};
|
||||
try {
|
||||
const response = await axios(config);
|
||||
return response.data.data;
|
||||
} catch (err) {
|
||||
console.error('Error seeding database:', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -13,15 +13,72 @@ const loading = ref(false);
|
||||
const saving = ref(false);
|
||||
const settings = ref([]);
|
||||
|
||||
// Seeder State
|
||||
const seedStatus = ref({ isSeeded: false, seededAt: null });
|
||||
const isSeeding = ref(false);
|
||||
const seedLoading = ref(false);
|
||||
|
||||
const methodOptions = [
|
||||
{ label: 'Typesense', value: 'typesense', icon: 'pi pi-bolt', class: 'text-amber-500' },
|
||||
{ label: 'MongoDB', value: 'mongo', icon: 'pi pi-database', class: 'text-emerald-500' }
|
||||
];
|
||||
|
||||
onMounted(async () => {
|
||||
await loadSettings();
|
||||
await Promise.all([loadSettings(), loadSeedStatus()]);
|
||||
});
|
||||
|
||||
const loadSeedStatus = async () => {
|
||||
seedLoading.value = true;
|
||||
try {
|
||||
const data = await SettingService.getSeedStatus();
|
||||
seedStatus.value = data || { isSeeded: false, seededAt: null };
|
||||
} catch (e) {
|
||||
console.warn('Failed to load seed status:', e);
|
||||
} finally {
|
||||
seedLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleSeedDatabase = () => {
|
||||
if (seedStatus.value.isSeeded) return;
|
||||
|
||||
confirm.require({
|
||||
message: 'توجه: این عملیات دادههای اولیه سیستم (کاربران پیشفرض، نقشها، دستهبندیها، ویژگیهای تکمیلی و وضعیتهای املاک) را در پایگاه داده ایجاد میکند. این عملیات فقط یکبار قابل اجراست و پس از انجام دکمه برای همیشه قفل خواهد شد. آیا ادامه میدهید؟',
|
||||
header: 'تایید مقداردهی اولیه پایگاه داده (Seed Database)',
|
||||
icon: 'pi pi-exclamation-triangle',
|
||||
rejectProps: { label: 'انصراف', severity: 'secondary', outlined: true },
|
||||
acceptProps: { label: 'تایید و شروع مقداردهی', severity: 'primary' },
|
||||
accept: async () => {
|
||||
isSeeding.value = true;
|
||||
try {
|
||||
const res = await SettingService.seedDatabase();
|
||||
seedStatus.value = {
|
||||
isSeeded: true,
|
||||
seededAt: res.seededAt || new Date()
|
||||
};
|
||||
toast.add({
|
||||
severity: 'success',
|
||||
summary: 'موفقیتآمیز',
|
||||
detail: res.message || 'پایگاه داده با موفقیت مقداردهی شد و قفل گردید.',
|
||||
life: 5000
|
||||
});
|
||||
await loadSettings();
|
||||
} catch (err) {
|
||||
const errMsg = err?.response?.data?.message || err.message || 'خطا در مقداردهی پایگاه داده';
|
||||
toast.add({
|
||||
severity: 'error',
|
||||
summary: 'خطا در مقداردهی',
|
||||
detail: errMsg,
|
||||
life: 4000
|
||||
});
|
||||
await loadSeedStatus();
|
||||
} finally {
|
||||
isSeeding.value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const loadSettings = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
@@ -151,6 +208,74 @@ const navigateToModule = (routePath) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── Database Seeder Section (One-Time Execution) ── -->
|
||||
<div class="card p-6 mb-6 shadow-sm border border-surface-200 dark:border-surface-700 bg-surface-0 dark:bg-surface-900 rounded-2xl">
|
||||
<div class="flex flex-col lg:flex-row lg:items-center justify-between gap-6">
|
||||
<div class="flex items-start gap-4">
|
||||
<div
|
||||
class="w-14 h-14 rounded-2xl flex items-center justify-center text-2xl flex-shrink-0 transition-colors"
|
||||
:class="seedStatus.isSeeded
|
||||
? 'bg-surface-100 text-surface-500 dark:bg-surface-800 dark:text-surface-400'
|
||||
: 'bg-indigo-100 text-indigo-600 dark:bg-indigo-950 dark:text-indigo-400 shadow-sm'"
|
||||
>
|
||||
<i :class="seedStatus.isSeeded ? 'pi pi-lock' : 'pi pi-database'"></i>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex items-center gap-2.5 flex-wrap">
|
||||
<h3 class="text-xl font-bold m-0 text-surface-900 dark:text-surface-0">
|
||||
مقداردهی اولیه پایگاه داده (Seed Database)
|
||||
</h3>
|
||||
<Tag
|
||||
v-if="seedStatus.isSeeded"
|
||||
value="مقداردهی شده و قفل است"
|
||||
severity="success"
|
||||
icon="pi pi-lock"
|
||||
rounded
|
||||
class="px-2.5 text-xs font-bold"
|
||||
/>
|
||||
<Tag
|
||||
v-else
|
||||
value="آماده مقداردهی (یکبار مصرف)"
|
||||
severity="warn"
|
||||
icon="pi pi-exclamation-circle"
|
||||
rounded
|
||||
class="px-2.5 text-xs font-bold"
|
||||
/>
|
||||
</div>
|
||||
<p class="text-surface-500 dark:text-surface-400 m-0 mt-1.5 text-sm leading-relaxed max-w-2xl">
|
||||
ایجاد خودکار کاربران اولیه، نقشها و سطوح دسترسی، دستهبندیهای املاک، ویژگیهای تکمیلی و وضعیتهای املاک در دیتابیس. این عملیات صرفاً یکبار قابل اجراست و پس از انجام دکمه برای همیشه قفل میشود.
|
||||
</p>
|
||||
<div v-if="seedStatus.isSeeded && seedStatus.seededAt" class="mt-2 text-xs text-surface-400 flex items-center gap-1.5 font-mono">
|
||||
<i class="pi pi-calendar text-xs"></i>
|
||||
<span>تاریخ اجرا: {{ new Date(seedStatus.seededAt).toLocaleString('fa-IR') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3 flex-shrink-0">
|
||||
<Button
|
||||
v-if="!seedStatus.isSeeded"
|
||||
label="اجرای مقداردهی اولیه دادهها"
|
||||
icon="pi pi-database"
|
||||
severity="primary"
|
||||
rounded
|
||||
:loading="isSeeding"
|
||||
@click="handleSeedDatabase"
|
||||
class="px-5 font-semibold"
|
||||
/>
|
||||
<Button
|
||||
v-else
|
||||
label="پایگاه داده مقداردهی شده است"
|
||||
icon="pi pi-lock"
|
||||
severity="secondary"
|
||||
rounded
|
||||
disabled
|
||||
class="px-5 font-semibold opacity-70 cursor-not-allowed"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── Quick Actions Toolbar ── -->
|
||||
<div class="card p-4 mb-6 flex flex-wrap items-center justify-between gap-4">
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
|
||||
Reference in New Issue
Block a user