Merge branch 'master' of github.com:akaunting/akaunting

This commit is contained in:
Cüneyt Şentürk 2022-09-15 01:17:27 +03:00
commit 8ef6f743bd
14 changed files with 125 additions and 44 deletions

View File

@ -0,0 +1,10 @@
<?php
namespace App\Exceptions\Common;
use Illuminate\Http\Exceptions\ThrottleRequestsException;
class TooManyEmailsSent extends ThrottleRequestsException
{
//
}

View File

@ -7,9 +7,13 @@ use App\Models\Document\Document;
use App\Notifications\Sale\Invoice as Notification;
use App\Jobs\Document\SendDocumentAsCustomMail;
use App\Http\Requests\Common\CustomMail as Request;
use Illuminate\Http\JsonResponse;
use App\Traits\Emails;
class InvoiceEmails extends Controller
{
use Emails;
/**
* Instantiate a new controller instance.
*/
@ -22,14 +26,7 @@ class InvoiceEmails extends Controller
$this->middleware('permission:delete-sales-invoices')->only('destroy');
}
/**
* Show the form for creating a new resource.
*
* @param Document $invoice
*
* @return Response
*/
public function create(Document $invoice)
public function create(Document $invoice): JsonResponse
{
$notification = new Notification($invoice, 'invoice_new_customer', true);
@ -58,16 +55,9 @@ class InvoiceEmails extends Controller
]);
}
/**
* Store a newly created resource in storage.
*
* @param Request $request
*
* @return Response
*/
public function store(Request $request)
public function store(Request $request): JsonResponse
{
$response = $this->ajaxDispatch(new SendDocumentAsCustomMail($request, 'invoice_new_customer'));
$response = $this->sendEmail(new SendDocumentAsCustomMail($request, 'invoice_new_customer'));
if ($response['success']) {
$invoice = Document::find($request->get('document_id'));

View File

@ -8,9 +8,13 @@ use App\Notifications\Portal\PaymentReceived as PaymentReceivedNotification;
use App\Notifications\Banking\Transaction as TransactionNotification;
use App\Jobs\Banking\SendTransactionAsCustomMail;
use App\Http\Requests\Common\CustomMail as Request;
use Illuminate\Http\JsonResponse;
use App\Traits\Emails;
class TransactionEmails extends Controller
{
use Emails;
/**
* Instantiate a new controller instance.
*/
@ -23,14 +27,7 @@ class TransactionEmails extends Controller
$this->middleware('permission:delete-banking-transactions')->only('destroy');
}
/**
* Show the form for creating a new resource.
*
* @param Transaction $transaction
*
* @return Response
*/
public function create(Transaction $transaction)
public function create(Transaction $transaction): JsonResponse
{
$email_template = config('type.transaction.' . $transaction->type . '.email_template');
@ -73,20 +70,13 @@ class TransactionEmails extends Controller
]);
}
/**
* Store a newly created resource in storage.
*
* @param Request $request
*
* @return Response
*/
public function store(Request $request)
public function store(Request $request): JsonResponse
{
$transaction = Transaction::find($request->get('transaction_id'));
$email_template = config('type.transaction.' . $transaction->type . '.email_template');
$response = $this->ajaxDispatch(new SendTransactionAsCustomMail($request, $email_template));
$response = $this->sendEmail(new SendTransactionAsCustomMail($request, $email_template));
if ($response['success']) {
$route = config('type.transaction.' . $transaction->type . '.route.prefix');

View File

@ -134,6 +134,10 @@ class Kernel extends HttpKernel
'import' => [
'throttle:import',
],
'email' => [
'throttle:email',
],
];
/**

View File

@ -289,5 +289,12 @@ class Route extends Provider
RateLimiter::for('import', function (Request $request) {
return Limit::perMinute(config('app.throttles.import'));
});
RateLimiter::for('email', function (Request $request) {
return [
Limit::perDay(config('app.throttles.email.month'), 30),
Limit::perMinute(config('app.throttles.email.minute')),
];
});
}
}

55
app/Traits/Emails.php Normal file
View File

@ -0,0 +1,55 @@
<?php
namespace App\Traits;
use App\Abstracts\Job;
use App\Exceptions\Common\TooManyEmailsSent;
use App\Traits\Jobs;
use Illuminate\Support\Facades\RateLimiter;
trait Emails
{
use Jobs;
public function sendEmail(Job $job): array
{
// Check if the user has reached the limit of emails per month
$key_per_month = 'email-month:' . user()->id;
$limit_per_month = config('app.throttles.email.month');
$decay_per_month = 60 * 60 * 24 * 30;
$can_send = RateLimiter::attempt($key_per_month, $limit_per_month, fn() => '', $decay_per_month);
if ($can_send) {
// Check if the user has reached the limit of emails per minute
$key_per_minute = 'email-minute:' . user()->id;
$limit_per_minute = config('app.throttles.email.minute');
$can_send = RateLimiter::attempt($key_per_minute, $limit_per_minute, fn() => '');
}
if ($can_send) {
$this->dispatch($job);
$response = [
'success' => true,
'error' => false,
'data' => '',
'message' => '',
];
return $response;
}
$response = [
'success' => false,
'error' => true,
'data' => null,
'message' => 'Too many emails sent!',
];
report(new TooManyEmailsSent('Too many emails sent!'));
return $response;
}
}

View File

@ -25,6 +25,10 @@ return [
'throttles' => [
'api' => env('APP_THROTTLES_API', '60'),
'import' => env('APP_THROTTLES_IMPORT', '1'),
'email' => [
'minute' => env('APP_THROTTLES_EMAIL_MINUTE', '3'),
'month' => env('APP_THROTTLES_EMAIL_MONTH', '500'),
],
],
/*

View File

@ -19,7 +19,7 @@
</x-dropdown.link>
<x-dropdown.link href="{{ route('transactions.create', ['type' => 'expense']) }}" kind="primary">
{{ trans('general.title.new', ['type' => trans_choice('general.expenses', 1)]) }}
{{ trans('general.title.new', ['type' => trans_choice('general.expenses', 1)]) }}
</x-dropdown.link>
</x-dropdown>
@endcan
@ -36,7 +36,7 @@
data-menu="profile-menu"
>
<span id="menu-profile-icon-cancel" name="account_circle" class="material-icons-outlined w-8 h-8 flex items-center justify-center text-purple text-2xl hidden">
account_circle
account_circle
</span>
@if (setting('default.use_gravatar', '0') == '1')

View File

@ -139,6 +139,9 @@
if (button.getAttribute("data-menu") !== menuRef && iconButton.children[0].textContent != "cancel") {
button.children[0].textContent = button.children[0].getAttribute("name");
button.children[0].classList.remove("active"); // inactive icon
let split_id = button.children[0].id.split("-cancel");
button.children[0].id = split_id[0];
}
}
});
@ -148,6 +151,7 @@
if (menu.classList.contains(menuRef) && iconButton.children[0].textContent != "cancel") {
iconButton.children[0].textContent = "cancel";
iconButton.children[0].classList.add("active");
iconButton.children[0].id += "-cancel";
menu.classList.remove("ltr:-left-80", "rtl:-right-80");
menu.classList.add("ltr:left-14", "rtl:right-14");
@ -177,6 +181,9 @@
iconButton.children[0].textContent = icon;
iconButton.children[0].classList.remove("active");
let split_id = iconButton.children[0].id.split("-cancel");
iconButton.children[0].id = split_id[0];
menu.classList.add("ltr:-left-80", "rtl:-right-80");
menu.classList.remove("ltr:left-14", "rtl:right-14");
@ -242,6 +249,7 @@
settings_icon_html.children[0].textContent = "cancel";
settings_icon_html.children[0].classList.add("active");
settings_icon_html.children[0].id += "-cancel";
toggleButton.classList.add("invisible");
}

View File

@ -26,6 +26,7 @@
<link rel="stylesheet" href="{{ asset('public/vendor/quicksand/css/quicksand.css?v=' . version('short')) }}" type="text/css">
<!-- Css -->
<link rel="stylesheet" href="{{ asset('public/css//third_party/swiper-bundle.min.css?v=' . version('short')) }}" type="text/css">
<link rel="stylesheet" href="{{ asset('public/css/element.css?v=' . version('short')) }}" type="text/css">
<link rel="stylesheet" href="{{ asset('public/css/app.css?v=' . version('short')) }}" type="text/css">

View File

@ -122,6 +122,9 @@
if (button.getAttribute("data-menu") !== menuRef && iconButton.children[0].textContent != "cancel") {
button.children[0].textContent = button.children[0].getAttribute("name");
button.children[0].classList.remove("active"); // inactive icon
let split_id = button.children[0].id.split("-cancel");
button.children[0].id = split_id[0];
}
}
});
@ -131,6 +134,7 @@
if (menu.classList.contains(menuRef) && iconButton.children[0].textContent != "cancel") {
iconButton.children[0].textContent = "cancel";
iconButton.children[0].classList.add("active");
iconButton.children[0].id += "-cancel";
menu.classList.remove("ltr:-left-80", "rtl:-right-80");
menu.classList.add("ltr:left-14", "rtl:right-14");
@ -155,6 +159,9 @@
iconButton.children[0].textContent = icon;
iconButton.children[0].classList.remove("active");
let split_id = iconButton.children[0].id.split("-cancel");
iconButton.children[0].id = split_id[0];
menu.classList.add("ltr:-left-80", "rtl:-right-80");
menu.classList.remove("ltr:left-14", "rtl:right-14");
mainContent.classList.remove("hidden");

View File

@ -26,6 +26,7 @@
<link rel="stylesheet" href="{{ asset('public/vendor/quicksand/css/quicksand.css?v=' . version('short')) }}" type="text/css">
<!-- Css -->
<link rel="stylesheet" href="{{ asset('public/css//third_party/swiper-bundle.min.css?v=' . version('short')) }}" type="text/css">
<link rel="stylesheet" href="{{ asset('public/css/element.css?v=' . version('short')) }}" type="text/css">
<link rel="stylesheet" href="{{ asset('public/css/app.css?v=' . version('short')) }}" type="text/css">

View File

@ -33,7 +33,7 @@
<div class="swiper-slide">
<x-tabs.nav
id="{{ $name }}"
@click="onChangePaymentMethodSigned('{{ $key }}')"
@click="onChangePaymentMethod('{{ $key }}')"
>
<div class="w-24 truncate">
{{ $name }}

View File

@ -259,9 +259,13 @@ Route::group(['as' => 'modals.', 'prefix' => 'modals'], function () {
'middleware' => ['date.format', 'money', 'dropzone']
]);
Route::resource('transactions/{transaction}/emails', 'Modals\TransactionEmails', ['names' => 'transactions.emails']);
Route::resource('transactions/{transaction}/share', 'Modals\TransactionShare', ['names' => 'transactions.share']);
Route::resource('invoices/{invoice}/emails', 'Modals\InvoiceEmails', ['names' => 'invoices.emails']);
Route::resource('invoices/{invoice}/share', 'Modals\InvoiceShare', ['names' => 'invoices.share']);
Route::get('invoices/{invoice}/emails/create', 'Modals\InvoiceEmails@create')->name('invoices.emails.create');
Route::post('invoices/{invoice}/emails', 'Modals\InvoiceEmails@store')->name('invoices.emails.store');
Route::get('invoices/{invoice}/share/create', 'Modals\InvoiceShare@create')->name('invoices.share.create');
Route::get('transactions/{transaction}/emails/create', 'Modals\TransactionEmails@create')->name('transactions.emails.create');
Route::post('transactions/{transaction}/emails', 'Modals\TransactionEmails@store')->name('transactions.emails.store');
Route::get('transactions/{transaction}/share/create', 'Modals\TransactionShare@create')->name('transactions.share.create');
Route::resource('taxes', 'Modals\Taxes');
});