improved email setup in admin settings

This commit is contained in:
Čarodej
2022-02-11 10:52:14 +01:00
parent 03730b80c0
commit 3fd43936e7
13 changed files with 329 additions and 222 deletions

View File

@@ -10,7 +10,7 @@ LOG_CHANNEL=daily
SCOUT_DRIVER=tntsearch
SCOUT_QUEUE=true
FILESYSTEM_DRIVER=
FILESYSTEM_DRIVER=local
CHUNK_SIZE=128
DB_CONNECTION=mysql

View File

@@ -1,5 +1,3 @@
[![Frontend Build](https://github.com/MakingCG/vuefilemanager/actions/workflows/build.yml/badge.svg)](https://github.com/MakingCG/vuefilemanager/actions/workflows/build.yml)
[![Unit Testing](https://github.com/MakingCG/vuefilemanager/actions/workflows/unit-testing.yml/badge.svg)](https://github.com/MakingCG/vuefilemanager/actions/workflows/unit-testing.yml)
![logo](https://vuefilemanager.com/assets/images/vuefilemanager-horizontal-logo.svg)
# Private Cloud Storage Build on Laravel & Vue.js
@@ -33,7 +31,7 @@
**For running app make sure you have installed:**
- PHP >= 7.3 version
- PHP >= 8.0.2 version
- MySQL 5.6+
- Nginx or Apache
@@ -176,49 +174,15 @@ These instructions is applicable for all updates. Please follow this step:
- Just rewrite all project files with new excluded `/.env` file and `/storage` folder. These items must be preserved!
# Payments
VueFileManager is packed with **Stripe** payment options. To configure Stripe, you will be asked in Setup Wizard to set up. Or, if you skip this installation process, you will find stripe set up in you admin `Dashboard / Settings / Payments`.
## Manage Failed Payments
VueFileManager manage failed payments with additional email notification. But, there is more you can do for better User Experience. There is some additionals option in Stripe, look on [prevent failed payments](https://dashboard.stripe.com/settings/billing/automatic).
## Tax Rates
You are able to manage tax rates. When adding a new tax rate, if no Region is specified, the tax rate will apply to everyone. Add a [ISO 3166-1 alpha-2 country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements) to the Region field if you wish to apply taxes per country.
Just log in to your stripe dashboard, and you will find taxes under `Dashboard / Products / Tax Rates`.
# Developers
## Running development environment on your localhost
When you download repository from GitHub, you have to rename your `.env.example` file to `.env`. Then run command below in your terminal to install vendors. Composer is required.
```
composer install
```
Set your `APP_ENV` to local mode, in default, it's in production mode.
```
APP_ENV=local
```
Also, to debug application, set `APP_DEBUG` on true:
```
APP_DEBUG=true
```
To start server on your localhost, run command below. Then go to your generated localhost URL by terminal, and follow Setup Wizard steps to configure VueFileManager.
```
php artisan serve
```
After successfully installation via Setup Wizard, stop your artisan server, clear config cache and run your artisan server again:
```
php artisan config:clear
php artisan serve
```
*After any change in your .env you have to restart your artisan server to reload your config cache.*
To develop your Vue front-end, you have to install npm modules by this command:
For developing Vue front-end, you have to install npm modules by this command:
```
npm install
```

View File

@@ -1,6 +1,38 @@
import i18n from '../../i18n'
const defaultState = {
mailEncryptionList: [
{
label: 'TLS',
value: 'tls',
},
{
label: 'SSL',
value: 'ssl',
},
],
mailDriverList: [
{
label: 'SMTP',
value: 'smtp',
},
{
label: 'Mailgun',
value: 'mailgun',
},
{
label: 'SES',
value: 'ses',
},
{
label: 'Postmark',
value: 'postmark',
},
{
label: 'Log',
value: 'log',
},
],
transactionColumns: [
{
label: i18n.t('Note'),
@@ -1023,9 +1055,11 @@ const defaultState = {
}
const getters = {
mailEncryptionList: (state) => state.mailEncryptionList,
transactionColumns: (state) => state.transactionColumns,
subscriptionTypes: (state) => state.subscriptionTypes,
teamPermissions: (state) => state.teamPermissions,
mailDriverList: (state) => state.mailDriverList,
expirationList: (state) => state.expirationList,
currencyList: (state) => state.currencyList,
intervalList: (state) => state.intervalList,

View File

@@ -2,70 +2,100 @@
<PageTab :is-loading="isLoading">
<ValidationObserver @submit.prevent="EmailSetupSubmit" ref="EmailSetup" v-slot="{ invalid }" tag="form" class="card shadow-card">
<FormLabel>{{ $t('admin_settings.email.section_email') }}</FormLabel>
<InfoBox>
<p v-html="$t('admin_settings.email.email_disclaimer')"></p>
</InfoBox>
<ValidationProvider tag="div" mode="passive" name="Mail Driver" rules="required" v-slot="{ errors }">
<AppInputText :title="$t('admin_settings.email.driver')" :error="errors[0]">
<input
v-model="mail.driver"
:placeholder="$t('admin_settings.email.driver_plac')"
type="text"
:class="{ 'border-red': errors[0] }"
class="focus-border-theme input-dark"
/>
</AppInputText>
</ValidationProvider>
<ValidationProvider tag="div" mode="passive" name="Mail Host" rules="required" v-slot="{ errors }">
<AppInputText :title="$t('admin_settings.email.host')" :error="errors[0]">
<input
v-model="mail.host"
:placeholder="$t('admin_settings.email.host_plac')"
type="text"
:class="{ 'border-red': errors[0] }"
class="focus-border-theme input-dark"
/>
</AppInputText>
</ValidationProvider>
<ValidationProvider tag="div" mode="passive" name="Mail Port" rules="required" v-slot="{ errors }">
<AppInputText :title="$t('admin_settings.email.port')" :error="errors[0]">
<input
v-model="mail.port"
:placeholder="$t('admin_settings.email.port_plac')"
type="text"
:class="{ 'border-red': errors[0] }"
class="focus-border-theme input-dark"
/>
</AppInputText>
</ValidationProvider>
<ValidationProvider tag="div" mode="passive" name="Mail Username" rules="required" v-slot="{ errors }">
<AppInputText :title="$t('admin_settings.email.username')" :error="errors[0]">
<input
v-model="mail.username"
:placeholder="$t('admin_settings.email.username_plac')"
type="text"
:class="{ 'border-red': errors[0] }"
class="focus-border-theme input-dark"
/>
</AppInputText>
</ValidationProvider>
<ValidationProvider tag="div" mode="passive" name="Mail Password" rules="required" v-slot="{ errors }">
<AppInputText :title="$t('admin_settings.email.password')" :error="errors[0]">
<input
v-model="mail.password"
:placeholder="$t('admin_settings.email.password_plac')"
type="text"
:class="{ 'border-red': errors[0] }"
class="focus-border-theme input-dark"
/>
</AppInputText>
</ValidationProvider>
<ValidationProvider tag="div" mode="passive" name="Mail Encryption" rules="required" v-slot="{ errors }">
<AppInputText :title="$t('admin_settings.email.encryption')" :error="errors[0]">
<SelectInput v-model="mail.encryption" :options="encryptionList" :placeholder="$t('admin_settings.email.encryption_plac')" :isError="errors[0]" />
</AppInputText>
</ValidationProvider>
<ButtonBase :loading="isSendingRequest" :disabled="isSendingRequest" type="submit" button-style="theme" class="submit-button">
<ValidationProvider tag="div" mode="passive" name="Mail Driver" rules="required" v-slot="{ errors }">
<AppInputText title="Mail Driver" :error="errors[0]">
<SelectInput v-model="mailDriver" :default="mailDriver" :options="mailDriverList" placeholder="Select your mail driver" :isError="errors[0]" />
</AppInputText>
</ValidationProvider>
<div v-if="mailDriver === 'smtp'">
<ValidationProvider tag="div" mode="passive" name="Mail Host" rules="required" v-slot="{ errors }">
<AppInputText title="Mail Host" :error="errors[0]">
<input class="focus-border-theme input-dark" v-model="smtp.host" placeholder="Type your mail host" type="text" :class="{ 'border-red': errors[0] }" />
</AppInputText>
</ValidationProvider>
<ValidationProvider tag="div" mode="passive" name="Mail Port" rules="required" v-slot="{ errors }">
<AppInputText title="Mail Port" :error="errors[0]">
<input class="focus-border-theme input-dark" v-model="smtp.port" placeholder="Type your mail port" type="text" :class="{ 'border-red': errors[0] }" />
</AppInputText>
</ValidationProvider>
<ValidationProvider tag="div" mode="passive" name="Mail Username" rules="required" v-slot="{ errors }">
<AppInputText title="Mail Username" :error="errors[0]">
<input class="focus-border-theme input-dark" v-model="smtp.username" placeholder="Type your mail username" type="text" :class="{ 'border-red': errors[0] }" />
</AppInputText>
</ValidationProvider>
<ValidationProvider tag="div" mode="passive" name="Mail Password" rules="required" v-slot="{ errors }">
<AppInputText title="Mail Password" :error="errors[0]">
<input class="focus-border-theme input-dark" v-model="smtp.password" placeholder="Type your mail password" type="text" :class="{ 'border-red': errors[0] }" />
</AppInputText>
</ValidationProvider>
<ValidationProvider tag="div" mode="passive" name="Mail Encryption" rules="required" v-slot="{ errors }">
<AppInputText title="Mail Encryption" :error="errors[0]">
<SelectInput v-model="smtp.encryption" :default="smtp.encryption" :options="mailEncryptionList" placeholder="Select your mail encryption" :isError="errors[0]" />
</AppInputText>
</ValidationProvider>
</div>
<div v-if="mailDriver === 'mailgun'">
<ValidationProvider tag="div" mode="passive" name="Domain" rules="required" v-slot="{ errors }">
<AppInputText title="Domain" :error="errors[0]">
<input class="focus-border-theme input-dark" v-model="mailgun.domain" placeholder="Type your domain" type="text" :class="{ 'border-red': errors[0] }" />
</AppInputText>
</ValidationProvider>
<ValidationProvider tag="div" mode="passive" name="Secret" rules="required" v-slot="{ errors }">
<AppInputText title="Secret" :error="errors[0]">
<input class="focus-border-theme input-dark" v-model="mailgun.secret" placeholder="Type your secret" type="text" :class="{ 'border-red': errors[0] }" />
</AppInputText>
</ValidationProvider>
<ValidationProvider tag="div" mode="passive" name="Endpoint" rules="required" v-slot="{ errors }">
<AppInputText title="Endpoint" :error="errors[0]">
<input class="focus-border-theme input-dark" v-model="mailgun.endpoint" placeholder="Type your endpoint" type="text" :class="{ 'border-red': errors[0] }" />
</AppInputText>
</ValidationProvider>
</div>
<div v-if="mailDriver === 'postmark'">
<ValidationProvider tag="div" mode="passive" name="Token" rules="required" v-slot="{ errors }">
<AppInputText title="Token" :error="errors[0]">
<input class="focus-border-theme input-dark" v-model="postmark.token" placeholder="Type your token" type="text" :class="{ 'border-red': errors[0] }" />
</AppInputText>
</ValidationProvider>
</div>
<div v-if="mailDriver === 'ses'">
<ValidationProvider tag="div" mode="passive" name="Access Key" rules="required" v-slot="{ errors }">
<AppInputText title="Access Key" :error="errors[0]">
<input class="focus-border-theme input-dark" v-model="ses.access_key" placeholder="Type your access key" type="text" :class="{ 'border-red': errors[0] }" />
</AppInputText>
</ValidationProvider>
<ValidationProvider tag="div" mode="passive" name="Secret Access Key" rules="required" v-slot="{ errors }">
<AppInputText title="Secret Access Key" :error="errors[0]">
<input class="focus-border-theme input-dark" v-model="ses.secret_access_key" placeholder="Type your secret access key" type="text" :class="{ 'border-red': errors[0] }" />
</AppInputText>
</ValidationProvider>
<ValidationProvider tag="div" mode="passive" name="Default Region" rules="required" v-slot="{ errors }">
<AppInputText title="Default Region" :error="errors[0]">
<input class="focus-border-theme input-dark" v-model="ses.default_region" placeholder="Type your default region" type="text" :class="{ 'border-red': errors[0] }" />
</AppInputText>
</ValidationProvider>
<ValidationProvider tag="div" mode="passive" name="Session Token" v-slot="{ errors }">
<AppInputText title="Session Token" :error="errors[0]">
<input class="focus-border-theme input-dark" v-model="ses.session_token" placeholder="Type your session token" type="text" :class="{ 'border-red': errors[0] }" />
</AppInputText>
</ValidationProvider>
</div>
<ButtonBase :loading="isSendingRequest" :disabled="isSendingRequest" type="submit" button-style="theme" class="w-full sm:w-auto">
{{ $t('admin_settings.email.save_button') }}
</ButtonBase>
</ValidationObserver>
@@ -86,6 +116,7 @@ import InfoBox from '../../../../components/Others/Forms/InfoBox'
import { required } from 'vee-validate/dist/rules'
import { events } from '../../../../bus'
import axios from 'axios'
import {mapGetters} from "vuex";
export default {
name: 'AppAppearance',
@@ -103,59 +134,78 @@ export default {
PageTab,
InfoBox,
},
computed: {
...mapGetters([
'mailEncryptionList',
'mailDriverList',
]),
},
data() {
return {
isLoading: false,
isSendingRequest: false,
encryptionList: [
{
label: 'TLS',
value: 'tls',
},
{
label: 'SSL',
value: 'ssl',
},
],
mail: {
driver: '',
host: '',
port: '',
username: '',
password: '',
encryption: '',
},
mailDriver: undefined,
ses: {
access_key: undefined,
secret_access_key: undefined,
default_region: undefined,
session_token: undefined,
},
smtp: {
host: undefined,
port: undefined,
username: undefined,
password: undefined,
encryption: undefined,
},
mailgun: {
domain: undefined,
secret: undefined,
endpoint: undefined,
},
postmark: {
token: undefined,
},
}
},
methods: {
async EmailSetupSubmit() {
// Validate fields
const isValid = await this.$refs.EmailSetup.validate()
if (!isValid) return
// Validate fields
const isValid = await this.$refs.EmailSetup.validate()
// Start loading
this.isSendingRequest = true
if (!isValid) return
// Send request to get verify account
axios
.post('/api/admin/settings/email', this.mail)
.then(() => {
events.$emit('toaster', {
type: 'success',
message: this.$t('toaster.email_set'),
})
})
.catch(() => {
events.$emit('alert:open', {
title: this.$t('popup_error.title'),
message: this.$t('popup_error.message'),
})
})
.finally(() => {
// End loading
this.isSendingRequest = false
})
// Start loading
this.isSendingRequest = true
// Send request to get verify account
axios
.post('/api/admin/settings/email', {
environment: this.environment,
storage: this.storage,
mailDriver: this.mailDriver,
smtp: this.smtp,
mailgun: this.mailgun,
ses: this.ses,
postmark: this.postmark,
})
.then(() => {
events.$emit('toaster', {
type: 'success',
message: this.$t('toaster.email_set'),
})
})
.catch((error) => {
events.$emit('alert:open', {
title: this.$t('popup_error.title'),
message: this.$t('popup_error.message'),
})
})
.finally(() => {
this.isSendingRequest = false
})
},
},
}

View File

@@ -87,7 +87,7 @@
<ValidationProvider tag="div" mode="passive" name="Mail Encryption" rules="required" v-slot="{ errors }">
<AppInputText title="Mail Encryption" :error="errors[0]" :is-last="true">
<SelectInput v-model="smtp.encryption" :default="smtp.encryption" :options="encryptionList" placeholder="Select your mail encryption" :isError="errors[0]" />
<SelectInput v-model="smtp.encryption" :default="smtp.encryption" :options="mailEncryptionList" placeholder="Select your mail encryption" :isError="errors[0]" />
</AppInputText>
</ValidationProvider>
</div>
@@ -180,6 +180,7 @@ import {SettingsIcon} from 'vue-feather-icons'
import Headline from '../Auth/Headline'
import {required} from 'vee-validate/dist/rules'
import axios from 'axios'
import {mapGetters} from "vuex";
export default {
name: 'EnvironmentSetup',
@@ -212,6 +213,10 @@ export default {
},
},
computed: {
...mapGetters([
'mailEncryptionList',
'mailDriverList',
]),
regionList() {
return {
storj: this.storjRegions,
@@ -468,38 +473,6 @@ export default {
value: 'oss',
},
],
encryptionList: [
{
label: 'TLS',
value: 'tls',
},
{
label: 'SSL',
value: 'ssl',
},
],
mailDriverList: [
{
label: 'SMTP',
value: 'smtp',
},
{
label: 'Mailgun',
value: 'mailgun',
},
{
label: 'SES',
value: 'ses',
},
{
label: 'Postmark',
value: 'postmark',
},
{
label: 'Log',
value: 'log',
},
],
storage: {
driver: 'local',
key: undefined,

View File

@@ -2,7 +2,7 @@
use Domain\Admin\Controllers\Users\UserController;
use Domain\Pages\Controllers\AdminPagesController;
use Domain\Settings\Controllers\SetEmailController;
use Domain\Settings\Controllers\StoreEmailCredentialsController;
use Domain\Settings\Controllers\FlushCacheController;
use Domain\Localization\Controllers\LanguageController;
use Domain\Admin\Controllers\Users\DeleteUserController;
@@ -48,7 +48,7 @@ Route::group(['prefix' => 'settings'], function () {
Route::get('/', GetSettingsValueController::class);
Route::get('/flush-cache', FlushCacheController::class);
Route::post('/email', SetEmailController::class);
Route::post('/email', StoreEmailCredentialsController::class);
Route::post('/payment-service', StorePaymentServiceCredentialsController::class);
Route::post('/social-service', StoreSocialServiceCredentialsController::class);

View File

@@ -57,7 +57,7 @@ class SetupDevEnvironment extends Command
$this->info('Storing default settings and content...');
($this->seedDefaultPages)();
($this->seedDefaultSettings)($this->argument('type'));
($this->seedDefaultSettings)($this->argument('license'));
($this->seedDefaultLanguage)();
$this->store_default_settings();
@@ -82,6 +82,8 @@ class SetupDevEnvironment extends Command
'--stop-when-empty' => true,
]);
$this->warn('Please make sure your current host/domain where you are running app is included in your .env SANCTUM_STATEFUL_DOMAINS variable.');
$this->info('Everything is done, congratulations! 🥳🥳🥳');
}
@@ -1026,7 +1028,7 @@ class SetupDevEnvironment extends Command
],
[
'name' => 'license',
'value' => $this->argument('type'),
'value' => $this->argument('license'),
],
[
'name' => 'purchase_code',

View File

@@ -8,9 +8,12 @@ use Domain\Pages\Actions\SeedDefaultPagesAction;
use Domain\Settings\Actions\SeedDefaultSettingsAction;
use Domain\Localization\Actions\SeedDefaultLanguageAction;
use Domain\SetupWizard\Actions\CreateDiskDirectoriesAction;
use Illuminate\Foundation\Testing\WithFaker;
class SetupProdEnvironment extends Command
{
use WithFaker;
/**
* The name and signature of the console command.
*/
@@ -30,6 +33,7 @@ class SetupProdEnvironment extends Command
private SeedDefaultPagesAction $seedDefaultPages,
) {
parent::__construct();
$this->setUpFaker();
}
/**
@@ -51,7 +55,7 @@ class SetupProdEnvironment extends Command
$this->store_default_settings();
($this->seedDefaultPages)();
($this->seedDefaultSettings)($this->argument('type'));
($this->seedDefaultSettings)($this->argument('license'));
($this->seedDefaultLanguage)();
$this->info('Creating default admin...');
@@ -60,6 +64,8 @@ class SetupProdEnvironment extends Command
$this->info('Clearing application cache...');
$this->clear_cache();
$this->warn('Please make sure your current host/domain where you are running app is included in your .env SANCTUM_STATEFUL_DOMAINS variable.');
$this->info('Everything is done, congratulations! 🥳🥳🥳');
}
@@ -136,7 +142,7 @@ class SetupProdEnvironment extends Command
],
[
'name' => 'license',
'value' => $this->argument('type'),
'value' => $this->argument('license'),
],
[
'name' => 'purchase_code',
@@ -197,7 +203,15 @@ class SetupProdEnvironment extends Command
$user
->settings()
->create([
'name' => 'Admin',
'first_name' => 'Jane',
'last_name' => 'Doe',
'address' => $this->faker->address,
'state' => $this->faker->state,
'city' => $this->faker->city,
'postal_code' => $this->faker->postcode,
'country' => $this->faker->randomElement(['SK', 'CZ', 'DE', 'FR']),
'phone_number' => $this->faker->phoneNumber,
'timezone' => $this->faker->randomElement(['+1.0', '+2.0', '+3.0']),
]);
// Show user credentials
@@ -218,6 +232,12 @@ class SetupProdEnvironment extends Command
$this->call('key:generate', [
'--force' => true,
]);
$currentHost = request()->getHost() . ',' . request()->getHost() . ':' . request()->getPort();
setEnvironmentValue([
'SANCTUM_STATEFUL_DOMAINS' => "localhost,localhost:8000,127.0.0.1,127.0.0.1:8000,::1,$currentHost",
]);
}
/**

View File

@@ -1,36 +0,0 @@
<?php
namespace Domain\Settings\Controllers;
use Artisan;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
class SetEmailController
{
/**
* Set new email credentials to .env file
*/
public function __invoke(Request $request): Response
{
// TODO: pridat validator do requestu
// Abort in demo mode
abort_if(is_demo(), 204, 'Done.');
if (! app()->runningUnitTests()) {
setEnvironmentValue([
'MAIL_DRIVER' => $request->input('driver'),
'MAIL_HOST' => $request->input('host'),
'MAIL_PORT' => $request->input('port'),
'MAIL_USERNAME' => $request->input('username'),
'MAIL_PASSWORD' => $request->input('password'),
'MAIL_ENCRYPTION' => $request->input('encryption'),
]);
// Clear config cache
Artisan::call('config:clear');
Artisan::call('config:cache');
}
return response('Done', 204);
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace Domain\Settings\Controllers;
use Artisan;
use Domain\Settings\Requests\StoreEmailCredentialsRequest;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
class StoreEmailCredentialsController
{
/**
* Set new email credentials to .env file
*/
public function __invoke(StoreEmailCredentialsRequest $request): Response
{
// Abort in demo mode
abort_if(is_demo(), 204, 'Done.');
if (!app()->runningUnitTests()) {
$mail = [
'log' => [
'MAIL_DRIVER' => 'log',
],
'postmark' => [
'MAIL_DRIVER' => 'postmark',
'POSTMARK_TOKEN' => $request->input('postmark.token'),
],
'smtp' => [
'MAIL_DRIVER' => 'smtp',
'MAIL_HOST' => $request->input('smtp.host'),
'MAIL_PORT' => $request->input('smtp.port'),
'MAIL_USERNAME' => $request->input('smtp.username'),
'MAIL_PASSWORD' => $request->input('smtp.password'),
'MAIL_ENCRYPTION' => $request->input('smtp.encryption'),
],
'ses' => [
'MAIL_DRIVER' => 'ses',
'AWS_ACCESS_KEY_ID' => $request->input('ses.access_key'),
'AWS_SECRET_ACCESS_KEY' => $request->input('ses.secret_access_key'),
'AWS_DEFAULT_REGION' => $request->input('ses.default_region'),
'AWS_SESSION_TOKEN' => $request->input('ses.session_token'),
],
'mailgun' => [
'MAIL_DRIVER' => 'mailgun',
'MAILGUN_DOMAIN' => $request->input('mailgun.domain'),
'MAILGUN_SECRET' => $request->input('mailgun.secret'),
'MAILGUN_ENDPOINT' => $request->input('mailgun.endpoint'),
],
];
// Store credentials for mail
setEnvironmentValue(
$mail[$request->input('mailDriver')]
);
// Clear config cache
Artisan::call('config:clear');
Artisan::call('config:cache');
}
return response('Done', 204);
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Domain\Settings\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreEmailCredentialsRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'mailDriver' => 'required|string',
'smtp' => 'sometimes|array',
'ses' => 'sometimes|array',
'mailgun' => 'sometimes|array',
'postmark' => 'sometimes|array',
];
}
}

View File

@@ -44,6 +44,7 @@ class StoreEnvironmentSettingsController extends Controller
'MAIL_DRIVER' => 'log',
],
'postmark' => [
'MAIL_DRIVER' => 'postmark',
'POSTMARK_TOKEN' => $request->input('postmark.token'),
],
'smtp' => [

View File

@@ -33,9 +33,10 @@ class StoreEnvironmentSetupRequest extends FormRequest
'storage.region' => 'sometimes|nullable|string',
'storage.bucket' => 'sometimes|nullable|string',
'mailDriver' => 'required|string',
'mail' => 'sometimes|array',
'smtp' => 'sometimes|array',
'ses' => 'sometimes|array',
'mailgun' => 'sometimes|array',
'postmark' => 'sometimes|array',
];
}
}