mirror of
https://github.com/InvoiceShelf/InvoiceShelf.git
synced 2026-04-07 21:44:51 +00:00
* docs: add CLAUDE.md for Claude Code guidance * fix: handle missing settings table in installation middlewares RedirectIfInstalled crashed with "no such table: settings" when the database_created marker file existed but the database was empty. Changed to use isDbCreated() which verifies actual tables, and added try-catch around Setting queries in both middlewares. * feat: pre-select database driver from env in installation wizard The database step now reads DB_CONNECTION from the environment and pre-selects the matching driver on load, including correct defaults for hostname and port. * feat: pre-select mail driver and config from env in installation wizard The email step now fetches the current mail configuration on load instead of hardcoding the driver to 'mail'. SMTP fields fall back to Laravel config values from the environment. * refactor: remove file-based DB marker in favor of direct DB checks The database_created marker file was a second source of truth that could drift out of sync with the actual database. InstallUtils now checks the database directly via Schema::hasTable which is cached per-request and handles all error cases gracefully.
151 lines
4.1 KiB
Vue
151 lines
4.1 KiB
Vue
<template>
|
|
<BaseWizardStep
|
|
:title="$t('wizard.database.database')"
|
|
:description="$t('wizard.database.desc')"
|
|
step-container="w-full p-8 mb-8 bg-white border border-gray-200 border-solid rounded md:w-full"
|
|
>
|
|
<component
|
|
:is="databaseData.database_connection"
|
|
:config-data="databaseData"
|
|
:is-saving="isSaving"
|
|
@on-change-driver="getDatabaseConfig"
|
|
@submit-data="next"
|
|
/>
|
|
</BaseWizardStep>
|
|
</template>
|
|
|
|
<script>
|
|
import { ref, computed, onMounted } from 'vue'
|
|
import Mysql from './database/MysqlDatabase.vue'
|
|
import Pgsql from './database/PgsqlDatabase.vue'
|
|
import Sqlite from './database/SqliteDatabase.vue'
|
|
import { useNotificationStore } from '@/scripts/stores/notification'
|
|
import { useInstallationStore } from '@/scripts/admin/stores/installation'
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
export default {
|
|
components: {
|
|
Mysql,
|
|
Pgsql,
|
|
Sqlite,
|
|
},
|
|
|
|
emits: ['next'],
|
|
|
|
setup(props, { emit }) {
|
|
const database_connection = ref('mysql')
|
|
const isSaving = ref(false)
|
|
const { t } = useI18n()
|
|
const { global } = window.i18n
|
|
|
|
const notificationStore = useNotificationStore()
|
|
const installationStore = useInstallationStore()
|
|
|
|
const databaseData = computed(() => {
|
|
installationStore.currentDataBaseData.app_locale = global.locale.value
|
|
return installationStore.currentDataBaseData
|
|
})
|
|
|
|
async function getDatabaseConfig(connection) {
|
|
let params = {}
|
|
if (connection) {
|
|
params.connection = connection
|
|
}
|
|
|
|
const res = await installationStore.fetchInstallationDatabase(params)
|
|
|
|
if (res.data.success) {
|
|
databaseData.value.database_connection =
|
|
res.data.config.database_connection
|
|
}
|
|
|
|
if (res.data.config.database_connection === 'sqlite') {
|
|
databaseData.value.database_name = res.data.config.database_name
|
|
} else {
|
|
databaseData.value.database_name = null
|
|
if (res.data.config.database_host) {
|
|
databaseData.value.database_hostname = res.data.config.database_host
|
|
}
|
|
if (res.data.config.database_port) {
|
|
databaseData.value.database_port = res.data.config.database_port
|
|
}
|
|
}
|
|
}
|
|
|
|
async function next(databaseData) {
|
|
isSaving.value = true
|
|
|
|
try {
|
|
let res = await installationStore.addInstallationDatabase(databaseData)
|
|
isSaving.value = false
|
|
|
|
if (res.data.success) {
|
|
await installationStore.addInstallationFinish()
|
|
|
|
emit('next', 3)
|
|
|
|
let language = {
|
|
profile_language: global.locale.value,
|
|
}
|
|
await installationStore.addInstallationLanguage(language)
|
|
|
|
|
|
notificationStore.showNotification({
|
|
type: 'success',
|
|
message: t('wizard.success.' + res.data.success),
|
|
})
|
|
|
|
return
|
|
} else if (res.data.error) {
|
|
if (res.data.requirement) {
|
|
notificationStore.showNotification({
|
|
type: 'error',
|
|
message: t('wizard.errors.' + res.data.error, {
|
|
version: res.data.requirement.minimum,
|
|
name: databaseData.value.database_connection,
|
|
}),
|
|
})
|
|
return
|
|
}
|
|
|
|
notificationStore.showNotification({
|
|
type: 'error',
|
|
message: t('wizard.errors.' + res.data.error),
|
|
})
|
|
} else if (res.data.errors) {
|
|
notificationStore.showNotification({
|
|
type: 'error',
|
|
message: res.data.errors[0],
|
|
})
|
|
} else if (res.data.error_message) {
|
|
notificationStore.showNotification({
|
|
type: 'error',
|
|
message: res.data.error_message,
|
|
})
|
|
}
|
|
} catch (e) {
|
|
notificationStore.showNotification({
|
|
type: 'error',
|
|
message: t('validation.something_went_wrong'),
|
|
})
|
|
isSaving.value = false
|
|
} finally {
|
|
isSaving.value = false
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
getDatabaseConfig()
|
|
})
|
|
|
|
return {
|
|
databaseData,
|
|
database_connection,
|
|
isSaving,
|
|
getDatabaseConfig,
|
|
next,
|
|
}
|
|
},
|
|
}
|
|
</script>
|