From 112cc56922ad7f91249f4c0f0e47866e6f4d970c Mon Sep 17 00:00:00 2001 From: Darko Gjorgjijoski Date: Sat, 11 Apr 2026 02:00:00 +0200 Subject: [PATCH] chore(infra): default mail driver to sendmail and expose Vue runtime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mail DEFAULT_DRIVER changes from smtp to sendmail; DRIVER_ORDER is reshuffled so sendmail is the head of the list on fresh installs. This matches what most self-hosted installs already have working out of the box — SMTP requires provider credentials the typical user doesn't have set up yet. The mail config description is rewritten to drop the 'Laravel' framework reference and to explicitly tell unsure users to leave it on sendmail. SiteApi::get() now catches GuzzleException (the broader interface) and returns null on network failure instead of bubbling the exception object — callers were treating a non-array return as 'marketplace unavailable' anyway, so null is the correct shape. main.ts exposes the Vue runtime on window.__invoiceshelf_vue so module JS (compiled against the host's Vue install) can call createApp / defineComponent without re-bundling Vue. invoiceshelf.css adds Tailwind source globs for Modules/**/*.{js,ts,vue,blade.php} so module-contributed classes are picked up by the host CSS pipeline. Installation wizard PreferencesView was already in the tree waiting for the API field rename (date_formats, time_zones, fiscal_years, languages) that landed in setting.service.ts; this commit catches both sides up together. --- app/Services/MailConfigurationService.php | 4 ++-- app/Traits/SiteApi.php | 6 +++--- lang/en.json | 2 +- resources/css/invoiceshelf.css | 3 +++ resources/scripts/InvoiceShelf.ts | 4 ---- resources/scripts/api/services/setting.service.ts | 2 +- .../company/settings/components/MailConfigurationForm.vue | 4 ++-- .../features/installation/views/PreferencesView.vue | 8 ++++---- resources/scripts/main.ts | 4 ++++ 9 files changed, 20 insertions(+), 17 deletions(-) diff --git a/app/Services/MailConfigurationService.php b/app/Services/MailConfigurationService.php index b39780f5..4049dfa5 100644 --- a/app/Services/MailConfigurationService.php +++ b/app/Services/MailConfigurationService.php @@ -14,16 +14,16 @@ use Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkTransportFactory; class MailConfigurationService { - public const DEFAULT_DRIVER = 'smtp'; + public const DEFAULT_DRIVER = 'sendmail'; private const GLOBAL_SCOPE = 'global'; private const COMPANY_SCOPE = 'company'; private const DRIVER_ORDER = [ + 'sendmail', 'smtp', 'mail', - 'sendmail', 'ses', 'mailgun', 'postmark', diff --git a/app/Traits/SiteApi.php b/app/Traits/SiteApi.php index a6c10599..9714a72d 100644 --- a/app/Traits/SiteApi.php +++ b/app/Traits/SiteApi.php @@ -4,7 +4,7 @@ namespace App\Traits; use App\Models\Setting; use GuzzleHttp\Client; -use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Exception\GuzzleException; // Implementation taken from Akaunting - https://github.com/akaunting/akaunting trait SiteApi @@ -26,8 +26,8 @@ trait SiteApi try { $result = $client->get($url, $data); - } catch (RequestException $e) { - $result = $e; + } catch (GuzzleException $e) { + $result = null; } return $result; diff --git a/lang/en.json b/lang/en.json index b8db0008..71794564 100644 --- a/lang/en.json +++ b/lang/en.json @@ -1652,7 +1652,7 @@ "from_name": "From Mail Name", "from_mail": "From Mail Address", "encryption": "Mail Encryption", - "mail_config_desc": "Configure the Laravel mail driver used to send emails from the application, including provider-specific credentials when needed.", + "mail_config_desc": "Configure the mail driver used to send emails from the application. If you are unsure how to configure this, leave it set to Sendmail.", "basic_settings": "Basic Settings", "advanced_settings": "Advanced Settings", "show_advanced_settings": "Show Advanced Settings", diff --git a/resources/css/invoiceshelf.css b/resources/css/invoiceshelf.css index bb9ea727..c05478ca 100644 --- a/resources/css/invoiceshelf.css +++ b/resources/css/invoiceshelf.css @@ -9,6 +9,9 @@ @source "../scripts/**/*.vue"; @source "../scripts/**/*.ts"; +@source "../../Modules/**/resources/js/**/*.vue"; +@source "../../Modules/**/resources/js/**/*.ts"; +@source "../../Modules/**/resources/dist/**/*.js"; @source "../../resources/views/**/*.php"; @theme inline { diff --git a/resources/scripts/InvoiceShelf.ts b/resources/scripts/InvoiceShelf.ts index 884c05a6..58877b1c 100644 --- a/resources/scripts/InvoiceShelf.ts +++ b/resources/scripts/InvoiceShelf.ts @@ -67,10 +67,6 @@ export default class InvoiceShelf { /** * Execute all registered boot callbacks, install plugins, * and mount the app to `document.body`. - * - * Async so the install wizard's pre-DB language choice can be loaded - * before the first render — see the `install_language` localStorage key - * set by features/installation/views/LanguageView.vue. */ async start(): Promise { // Execute boot callbacks so modules can register routes / components diff --git a/resources/scripts/api/services/setting.service.ts b/resources/scripts/api/services/setting.service.ts index 0f66cac7..f193aa25 100644 --- a/resources/scripts/api/services/setting.service.ts +++ b/resources/scripts/api/services/setting.service.ts @@ -61,7 +61,7 @@ export const settingService = { return data }, - async getTimezones(): Promise<{ time_zones: string[] }> { + async getTimezones(): Promise<{ time_zones: Array<{ key: string; value: string }> }> { const { data } = await client.get(API.TIMEZONES) return data }, diff --git a/resources/scripts/features/company/settings/components/MailConfigurationForm.vue b/resources/scripts/features/company/settings/components/MailConfigurationForm.vue index 4956ec04..3293ef58 100644 --- a/resources/scripts/features/company/settings/components/MailConfigurationForm.vue +++ b/resources/scripts/features/company/settings/components/MailConfigurationForm.vue @@ -43,7 +43,7 @@ const showAdvancedFields = ref(false) const mailConfig = reactive(createDefaultMailConfig()) -const fallbackDrivers: MailDriver[] = ['smtp', 'mail', 'sendmail'] +const fallbackDrivers: MailDriver[] = ['sendmail', 'smtp', 'mail'] const encryptionOptions: SelectOption[] = [ { label: 'None', value: 'none' }, { label: 'TLS', value: 'tls' }, @@ -165,7 +165,7 @@ watch( function createDefaultMailConfig(): MailConfig { return { - mail_driver: 'smtp', + mail_driver: 'sendmail', from_mail: '', from_name: '', mail_host: '', diff --git a/resources/scripts/features/installation/views/PreferencesView.vue b/resources/scripts/features/installation/views/PreferencesView.vue index eaf7e7e8..1df0e74d 100644 --- a/resources/scripts/features/installation/views/PreferencesView.vue +++ b/resources/scripts/features/installation/views/PreferencesView.vue @@ -237,10 +237,10 @@ onMounted(async () => { } } - dateFormats.value = dateRes.data.data ?? dateRes.data - timeZones.value = tzRes.data.data ?? tzRes.data - fiscalYears.value = fyRes.data.data ?? fyRes.data ?? [] - languages.value = langRes.data.data ?? langRes.data ?? [] + dateFormats.value = dateRes.data.date_formats ?? dateRes.data.data ?? dateRes.data + timeZones.value = tzRes.data.time_zones ?? tzRes.data.data ?? tzRes.data + fiscalYears.value = fyRes.data.fiscal_years ?? fyRes.data.data ?? fyRes.data ?? [] + languages.value = langRes.data.languages ?? langRes.data.data ?? langRes.data ?? [] } catch (error: unknown) { showRequestError(error) } finally { diff --git a/resources/scripts/main.ts b/resources/scripts/main.ts index fc3fa8a8..ded8a26e 100644 --- a/resources/scripts/main.ts +++ b/resources/scripts/main.ts @@ -1,8 +1,12 @@ import '../css/invoiceshelf.css' import 'v-tooltip/dist/v-tooltip.css' +import * as Vue from 'vue' import InvoiceShelf from './InvoiceShelf' +// Expose Vue runtime for module scripts that import from the shim. +;(window as Record).__invoiceshelf_vue = Vue + declare global { interface Window { InvoiceShelf: InvoiceShelf