masoud 4 years ago
parent
commit
68536ac2cf
  1. 23
      app/Channels/FcmChannel.php
  2. 132
      app/Channels/Messages/FcmMessage.php
  3. 104
      app/Console/Commands/CostCommand.php
  4. 11
      app/Enums/cruds.php
  5. 51
      app/Enums/tables.php
  6. 38
      app/Events/BusinessUserCreate.php
  7. 38
      app/Events/TagCreate.php
  8. 6
      app/Http/Controllers/FileController.php
  9. 2
      app/Http/Resources/FileResource.php
  10. 46
      app/Listeners/BusinessUserCreateNotif.php
  11. 36
      app/Listeners/NotifHandler.php
  12. 37
      app/Listeners/TagCreateNotif.php
  13. 1
      app/Models/Business.php
  14. 4
      app/Models/Cost.php
  15. 32
      app/Models/ReportableRelation.php
  16. 5
      app/Models/User.php
  17. 63
      app/Notifications/DBNotification.php
  18. 48
      app/Notifications/FcmNotification.php
  19. 87
      app/Notifications/MailNotification.php
  20. 9
      app/Providers/AppServiceProvider.php
  21. 2
      app/Providers/AuthServiceProvider.php
  22. 16
      app/Providers/EventServiceProvider.php
  23. 2
      composer.json
  24. 4
      config/mail.php
  25. 35
      database/migrations/2021_03_08_114700_create_notifications_table.php
  26. 9
      docker-compose.yml
  27. 19
      resources/lang/fa/auth.php
  28. 24
      resources/lang/fa/notification.php
  29. 19
      resources/lang/fa/pagination.php
  30. 22
      resources/lang/fa/passwords.php
  31. 189
      resources/lang/fa/validation.php

23
app/Channels/FcmChannel.php

@ -0,0 +1,23 @@
<?php
namespace App\Channels;
use Illuminate\Notifications\Notification;
class FcmChannel
{
/**
* Send the given notification.
*
* @param mixed $notifiable
* @param \Illuminate\Notifications\Notification $notification
* @return void
*/
public function send($notifiable, Notification $notification)
{
$message = $notification->toFcm($notifiable);
$recipients = $notifiable->routeNotificationFor('fcm', $notification);
}
}

132
app/Channels/Messages/FcmMessage.php

@ -0,0 +1,132 @@
<?php
namespace App\Channels\Messages;
class FcmMessage
{
/**
* The devices token to send the message from.
*
* @var array|string
*/
public $to;
/**
* The topic of the FCM message.
*
* @var array
*/
public $topic;
/**
* The data of the FCM message.
*
* @var array
*/
public $data;
/**
* The notification body of the FCM message.
*
* @var array
*/
public $notification;
/**
* The condition for receive the FCM message.
*
* @var array
*/
public $condition;
/**
* The priority of the FCM message.
*
* @var string
*/
public $priority = 'normal';
/**
* Set the devices token to send the message from.
*
* @param array|string $to
* @return $this
*/
public function to($to)
{
if (is_array($to) && count($to) === 1) {
$this->to = $to[0];
} else {
$this->to = $to;
}
return $this;
}
/**
* Set the topic of the FCM message.
*
* @param string $topic
* @return $this
*/
public function topic(string $topic)
{
$this->topic = $topic;
return $this;
}
/**
* Set the data of the FCM message.
*
* @param array $data
* @return $this
*/
public function data(array $data)
{
$this->data = $data;
return $this;
}
/**
* Set the notification of the FCM message.
*
* @param array $notification
* @return $this
*/
public function notification(array $notification)
{
$this->notification = $notification;
return $this;
}
/**
* Set the condition for receive the FCM message.
*
* @param string $condition
* @return $this
*/
public function condition(string $condition)
{
$this->condition = $condition;
return $this;
}
/**
* Set the priority of the FCM message.
*
* @param string $priority
* @return $this
*/
public function priority(string $priority)
{
$this->priority = $priority;
return $this;
}
}

104
app/Console/Commands/CostCommand.php

@ -4,78 +4,60 @@ namespace App\Console\Commands;
use Throwable;
use Carbon\Carbon;
use App\Models\Cost;
use App\Models\Business;
use Illuminate\Console\Command;
use Morilog\Jalali\CalendarUtils;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Storage;
class CostCommand extends Command
{
public const USER_FEE = 400;
public const FILE_FEE = [
200 => 0,
400 => 1000,
800 => 2000,
];
protected $signature = 'cost:work';
protected $description = 'Run the cost worker';
public function __construct()
{
parent::__construct();
}
public function handle()
{
while (true) {
$business = Business::find(221);
if ($business === null) {
$lock = Cache::get('lock', false);
if ($lock) {
$this->info('There is no business for auditing.');
continue;
}
$year = jdate()->getYear();
$month = jdate()->getMonth();
$day = jdate()->getDay();
$hour = jdate()->getHour();
$minute = jdate()->getMinute();
$second = jdate()->getSecond();
if (jdate()->getYear() > jdate($business->calculated_at)->getYear()) {
$year = jdate()->getYear();
$month = 1;
$day = 1;
$business = Business::orderBy('calculated_at')->first();
if ($business === null) {
continue;
}
if (
jdate()->getYear() > jdate($business->calculated_at)->getYear()
&&
jdate()->getMonth() > jdate($business->calculated_at)->getMonth()
) {
$year = jdate()->getYear();
$month = jdate()->getMonth();
$day = 1;
if ($business->calculated_at->isFuture()) {
continue;
}
$gDate = CalendarUtils::toGregorian($year, $month, $day);
$carbon = Carbon::createFromDate($gDate[0], $gDate[1], $gDate[2]);
$carbon->setTime($hour, $minute, $second);
$next_month = jdate($business->calculated_at)->addMonths();
[$year, $month, $day] = CalendarUtils::toGregorian(
$next_month->getYear(), $next_month->getMonth(), 1
);
$now = Carbon::createFromDate($year, $month, $day);
$now->setTime(0, 0, 0);
$now = $carbon;
if ($now->isFuture()) {
$now = Carbon::now();
}
// if calculated_at less than an hour stop
if ($now->diffInMinutes($business->calculated_at)) {
if ($now->diffInMinutes($business->calculated_at) <= 59) {
$this->info('Must be one hour after the last audit.');
Cache::put('lock', true, $now->diffInSeconds($business->calculated_at));
continue;
}
try {
DB::beginTransaction();
// Fixed amounts of expenses
$business->load('users', 'cost');
$business->load('users', 'files');
$costs = 0;
$costs += $this->calculateCostOfBusinessUsers($business, $now);
@ -86,11 +68,13 @@ class CostCommand extends Command
// make sure save the calculated_at
$business->update([
'wallet' => $business->wallet - $costs,
'calculated_at' => Carbon::now(),
'calculated_at' => $now,
]);
DB::commit();
$this->info("The business #{$business->id} was audited.");
} catch (Throwable $throwable) {
throw $throwable;
DB::rollback();
report($throwable);
continue;
@ -98,16 +82,18 @@ class CostCommand extends Command
}
}
public function calculateCostOfBusinessUsers($business, $until_now)
public function calculateCostOfBusinessUsers($business, $now)
{
$user_fee = enum('business.fee.user');
$calculated_at = $business->calculated_at;
$recorded_month = jdate($business->calculated_at)->format("Y-m-01");
if ($business->users->isEmpty()) {
return 0;
}
// get business employee
$users_cost = $business->cost
->where('type', '=', 'users')
->where('type', '=', Cost::USER_TYPE)
->where('fee', '=', $user_fee)
->where('month', '=', $recorded_month)
->where('amount', '=', $business->users->count())
@ -115,16 +101,16 @@ class CostCommand extends Command
if ($users_cost === null) {
$business->cost()->create([
'type' => 'users',
'type' => Cost::USER_TYPE,
'month' => $recorded_month,
'amount' => $business->users->count(),
'fee' => $user_fee,
'duration' => $duration = $until_now->diffInSeconds($calculated_at), // from the created_at time of the newset fifth user
'duration' => $duration = $now->diffInMinutes($business->calculated_at), // from the created_at time of the newset fifth user
'additional' => $business->users->pluck('id')->toArray(),
]);
} else {
$users_cost->update([
'duration' => $duration = $until_now->diffInMinutes($calculated_at) + $users_cost->duration, // last calc - (current month - now else last calc - end of the past month),
'duration' => $duration = $now->diffInMinutes($business->calculated_at) + $users_cost->duration, // last calc - (current month - now else last calc - end of the past month),
'additional' => $business->users->pluck('id')->toArray(),
]);
}
@ -132,7 +118,7 @@ class CostCommand extends Command
return $user_fee * $duration;
}
public function calculateCostOfBusinessFiles($business, $until_now)
public function calculateCostOfBusinessFiles($business, $now)
{
$file_fee = enum('business.fee.file');
$calculated_at = $business->calculated_at;
@ -140,31 +126,33 @@ class CostCommand extends Command
// do the math in php
if (intdiv($business->files_volume, 200) === 0) {
$pads = 0;
$packs = intdiv($business->files_volume, 200);
if ($packs === 0) {
return 0;
} else {
$pads = intdiv($business->files_volume, 200) - 1;
$packs--;
}
$files = $business->cost
->where('type', '=', 'files')
->where('type', '=', Cost::FILE_TYPE)
->where('fee', '=', $file_fee)
->where('month', '=', $recorded_month)
->where('amount', '=', $business->files_volume)
->where('amount', '=', $packs)
->first();
if ($files === null) {
$business->cost()->create([
'type' => 'files',
'type' => Cost::FILE_TYPE,
'month' => $recorded_month,
'amount' => $pads,
'amount' => $packs,
'fee' => $file_fee,
'duration' => $duration = $until_now->diffInMinutes($calculated_at), // how to determine the file?,
'duration' => $duration = $now->diffInMinutes($calculated_at), // how to determine the file?,
'additional' => ['volume' => $business->files_volume],
]);
} else {
$files->update([
'duration' => $duration = $until_now->diffInMinutes($calculated_at) + $files->duration, // last calc - (current month - now else last calc - end of the past month),,
'duration' => $duration = $now->diffInMinutes($calculated_at) + $files->duration, // last calc - (current month - now else last calc - end of the past month),,
'additional' => ['volume' => $business->files_volume],
]);
}

11
app/Enums/cruds.php

@ -0,0 +1,11 @@
<?php
return [
'inverse' => [
10 => ['name' => 'Create', 'singular_name' => 'create'],
20 => ['name' => 'Update', 'singular_name' => 'update'],
30 => ['name' => 'Delete', 'singular_name' => 'delete'],
],
];

51
app/Enums/tables.php

@ -2,55 +2,82 @@
return [
'inverse' => [
10 => ['name' => 'Businesses'],
20 => ['name' => 'Projects'],
30 => ['name' => 'Sprints'],
40 => ['name' => 'Statuses'],
50 => ['name' => 'Systems'],
60 => ['name' => 'Workflows'],
70 => ['name' => 'Tags'],
80 => ['name' => 'Tasks'],
90 => ['name' => 'Works'],
210 => ['name' => 'BusinessUser'],
220 => ['name' => 'ProjectUser'],
230 => ['name' => 'TagTask'],
],
//Main Table's
'businesses' => [
'id' => 10,
'name' => 'Businesses'
'name' => 'Businesses',
'singular_name' => 'Business',
],
'projects' => [
'id' => 20,
'name' => 'Projects'
'name' => 'Projects',
'singular_name' => 'Project',
],
'sprints' => [
'id' => 30,
'name' => 'Sprints'
'name' => 'Sprints',
'singular_name' => 'Sprint',
],
'statuses' => [
'id' => 40,
'name' => 'Statuses'
'name' => 'Statuses',
'singular_name' => 'Status',
],
'systems' => [
'id' => 50,
'name' => 'Systems'
'name' => 'Systems',
'singular_name' => 'System',
],
'workflows' => [
'id' => 60,
'name' => 'Workflows'
'name' => 'Workflows',
'singular_name' => 'Workflow',
],
'tags' => [
'id' => 70,
'name' => 'Tags'
'name' => 'Tags',
'singular_name' => 'Tag',
],
'tasks' => [
'id' => 80,
'name' => 'Tasks'
'name' => 'Tasks',
'singular_name' => 'Task',
],
'works' => [
'id' => 90,
'name' => 'Works'
'name' => 'Works',
'singular_name' => 'Work',
],
//Relation Table's
'business_user' => [
'id' => 210,
'name' => 'BusinessUser'
'name' => 'BusinessUser',
'singular_name' => 'BusinessUser',
],
'project_user' => [
'id' => 220,
'name' => 'ProjectUser'
'name' => 'ProjectUser',
'singular_name' => 'ProjectUser',
],
'tag_task' => [
'id' => 230,
'name' => 'TagTask'
'name' => 'TagTask',
'singular_name' => 'TagTask',
],
];

38
app/Events/BusinessUserCreate.php

@ -0,0 +1,38 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class BusinessUserCreate
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $message;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct($message)
{
$this->message = $message;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}

38
app/Events/TagCreate.php

@ -0,0 +1,38 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class TagCreate
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $message;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct($message)
{
$this->message = $message;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}

6
app/Http/Controllers/FileController.php

@ -9,8 +9,8 @@ use App\Models\Business;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use App\Http\Resources\FileResource;
use Illuminate\Support\Facades\Auth;
use App\HiLib\Resources\FileResource;
use Spatie\QueryBuilder\QueryBuilder;
use Spatie\QueryBuilder\AllowedFilter;
use Illuminate\Support\Facades\Storage;
@ -127,6 +127,10 @@ class FileController extends Controller
'description' => $request->description
]);
$business->update([
'files_volume' => $business->files_volume + $file_record->size
]);
return new FileResource($file_record);
}

2
app/Http/Resources/FileResource.php

@ -1,9 +1,7 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class FileResource extends JsonResource

46
app/Listeners/BusinessUserCreateNotif.php

@ -0,0 +1,46 @@
<?php
namespace App\Listeners;
use App\Events\BusinessUserCreate;
use App\Models\Business;
use App\Models\User;
use App\Notifications\DBNotification;
use App\Notifications\MailNotification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Notification;
class BusinessUserCreateNotif
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param BusinessUserCreate $event
* @return void
*/
public function handle(BusinessUserCreate $event)
{
$payload = $event->message;
$new_user = User::findOrFail($payload->data->original->user_id);
$owners = Business::findOrFail($payload->business)->owners()->where('id', '!=', $new_user->id)->get();
$notif = [
'body' => __('notification.'.$payload->data->table_name.'.'.enum('cruds.inverse.'.$payload->data->crud_id.'.singular_name'), ['business' => request('_business_info')['name'], 'user' => $new_user->name])
];
$users = $owners->prepend($new_user);
Notification::send($users, new MailNotification($notif));
Notification::send($users, new DBNotification($notif));
}
}

36
app/Listeners/NotifHandler.php

@ -0,0 +1,36 @@
<?php
namespace App\Listeners;
use App\Events\ModelSaved;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class NotifHandler
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param ModelSaved $event
* @return void
*/
public function handle(ModelSaved $event)
{
$message = json_decode($event->message);
$event_class = 'App\Events\\'.enum('tables.'.$message->data->table_name.'.singular_name').enum('cruds.inverse.'.$message->data->crud_id.'.name');
if (class_exists($event_class)) {
// event(new ('App\Events\\'.$event_class($message)));
$event_class::dispatch($message);
}
}
}

37
app/Listeners/TagCreateNotif.php

@ -0,0 +1,37 @@
<?php
namespace App\Listeners;
use App\Events\TagCreate;
use App\Models\User;
use App\Notifications\MailNotification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Notification;
class TagCreateNotif
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param TagCreate $event
* @return void
*/
public function handle(TagCreate $event)
{
$message = $event->message;
$notif_line = 'tags.create';
$users = User::where('id', '<', 10)->get();
Notification::send($users, new MailNotification($notif_line));
}
}

1
app/Models/Business.php

@ -42,6 +42,7 @@ class Business extends Model implements HasMedia
protected $casts = [
'has_avatar' => 'boolean',
'calculated_at' => 'datetime',
];
public function getValueOf(?string $key)

4
app/Models/Cost.php

@ -6,6 +6,10 @@ use App\Models\Model;
class Cost extends Model
{
public const USER_TYPE = 'users';
public const FILE_TYPE = 'files';
public $perPage = 12;
protected $fillable = [

32
app/Models/ReportableRelation.php

@ -4,6 +4,7 @@ namespace App\Models;
use Anik\Amqp\Exchange;
use Anik\Amqp\Facades\Amqp;
use App\Events\ModelSaved;
use PhpAmqpLib\Wire\AMQPTable;
use Anik\Amqp\PublishableMessage;
use Illuminate\Support\Facades\Auth;
@ -56,21 +57,22 @@ class ReportableRelation extends Pivot
'from' => env('CONTAINER_NAME'),
];
$message = new PublishableMessage(json_encode($payload));
$routers = [
"activity_exchange" => ["name" => "activity",],
"notif_exchange" => ["name" => "notif",],
"socket_exchange" => ["name" => "socket",],
];
foreach ($routers as $exchange => $properties) {
$message->setProperties(["application_headers" => new AMQPTable($properties)]);
$message->setExchange(new Exchange($exchange));
Amqp::publish($message, "");
}
ModelSaved::dispatch(json_encode($payload));
// $message = new PublishableMessage(json_encode($payload));
//
// $routers = [
// "activity_exchange" => ["name" => "activity",],
// "notif_exchange" => ["name" => "notif",],
// "socket_exchange" => ["name" => "socket",],
// ];
//
// foreach ($routers as $exchange => $properties) {
// $message->setProperties(["application_headers" => new AMQPTable($properties)]);
//
// $message->setExchange(new Exchange($exchange));
//
// Amqp::publish($message, "");
// }
}
public function properties()

5
app/Models/User.php

@ -8,8 +8,8 @@ use App\Models\SoftDeletes;
use Illuminate\Validation\Rule;
use Illuminate\Http\UploadedFile;
use Spatie\MediaLibrary\HasMedia;
use App\Models\ReportableRelation;
use Illuminate\Auth\Authenticatable;
use Illuminate\Notifications\Notifiable;
use Spatie\MediaLibrary\InteractsWithMedia;
use Illuminate\Foundation\Auth\Access\Authorizable;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
@ -18,7 +18,8 @@ use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
class User extends Model implements AuthenticatableContract, AuthorizableContract, HasMedia
{
use SoftDeletes,
use Notifiable,
SoftDeletes,
Authorizable,
Authenticatable,
InteractsWithMedia;

63
app/Notifications/DBNotification.php

@ -0,0 +1,63 @@
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class DBNotification extends Notification
{
use Queueable;
public $message;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct($message)
{
$this->message = $message;
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return ['database'];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->line('The introduction to the notification.')
->action('Notification Action', url('/'))
->line('Thank you for using our application!');
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
'data' => $this->message['body']
];
}
}

48
app/Notifications/FcmNotification.php

@ -0,0 +1,48 @@
<?php
namespace App\Notifications;
use App\Channels\Messages\FcmMessage;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class FcmNotification extends Notification
{
use Queueable;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return [FcmNotification::class];
}
/**
* Get the voice representation of the notification.
*
* @param mixed $notifiable
* @return FCMMessage
*/
public function toFcm($notifiable)
{
// return (new FcmMessage())
// ->to([])
}
}

87
app/Notifications/MailNotification.php

@ -0,0 +1,87 @@
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class MailNotification extends Notification //implements ShouldQueue
{
use Queueable;
public $message;
/**
* The name of the queue connection to use when queueing the notification.
*
* @var string
*/
// public $connection = 'redis';
/**
* If your queue connection's after_commit configuration option is set to true,
* indicate that a particular queued listener should be dispatched after all database transactions closed
*
* @var boolean
*/
// public $afterCommit = true;
/**
* Determine which queues should be used for notification.
*
* @return string
*/
// public $queue = 'mail-queue';
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct($message)
{
$this->message = $message;
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->line($this->message['body'])
->action('Notification Action', url('/'))
->line('Thank you for using our application!');
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}

9
app/Providers/AppServiceProvider.php

@ -2,6 +2,9 @@
namespace App\Providers;
use App\Channels\FcmChannel;
use Illuminate\Notifications\ChannelManager;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
@ -13,7 +16,11 @@ class AppServiceProvider extends ServiceProvider
*/
public function register()
{
//
Notification::resolved(function (ChannelManager $service) {
$service->extend('fcm', function ($app) {
return new FcmChannel(new HttpClient, config('fcm.key'));
});
});
}
/**

2
app/Providers/AuthServiceProvider.php

@ -40,7 +40,7 @@ class AuthServiceProvider extends ServiceProvider
'token' => $request->bearerToken(),
'agent' => $request->getAgent(),
'os' => $request->getOS(),
])->first();
])->firstOrFail();
return $fingerprint->user->setAttribute('token', $fingerprint->token);
});

16
app/Providers/EventServiceProvider.php

@ -2,8 +2,13 @@
namespace App\Providers;
use App\Events\BusinessUserCreate;
use App\Events\ModelSaved;
use App\Events\TagCreate;
use App\Listeners\ActivityRegistration;
use App\Listeners\BusinessUserCreateNotif;
use App\Listeners\NotifHandler;
use App\Listeners\TagCreateNotif;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
@ -21,8 +26,15 @@ class EventServiceProvider extends ServiceProvider
SendEmailVerificationNotification::class,
],
ModelSaved::class => [
ActivityRegistration::class
]
ActivityRegistration::class,
NotifHandler::class,
],
TagCreate::class => [
TagCreateNotif::class,
],
BusinessUserCreate::class => [
BusinessUserCreateNotif::class,
],
];
/**

2
composer.json

@ -20,6 +20,8 @@
"jenssegers/agent": "^2.6",
"laravel/socialite": "^5.1",
"laravel/framework": "^8.12",
"laravel/legacy-factories": "^1",
"fruitcake/laravel-cors": "^2.0",
"league/flysystem-aws-s3-v3": "~1.0",
"spatie/laravel-medialibrary": "^9.0",
"spatie/laravel-query-builder": "^3.3",

4
config/mail.php

@ -36,8 +36,8 @@ return [
'mailers' => [
'smtp' => [
'transport' => 'smtp',
'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
'port' => env('MAIL_PORT', 587),
'host' => env('MAIL_HOST', 'localhost'),
'port' => env('MAIL_PORT', 1025),
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
'username' => env('MAIL_USERNAME'),
'password' => env('MAIL_PASSWORD'),

35
database/migrations/2021_03_08_114700_create_notifications_table.php

@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateNotificationsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('notifications', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->string('type');
$table->morphs('notifiable');
$table->text('data');
$table->timestamp('read_at')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('notifications');
}
}

9
docker-compose.yml

@ -100,6 +100,15 @@ services:
- mysql
networks:
- sail
mailhog:
image: mailhog/mailhog
logging:
driver: 'none' # disable saving logs
ports:
- 1025:1025 # smtp server
- 8025:8025 # web ui
networks:
- sail
networks:
sail:
driver: bridge

19
resources/lang/fa/auth.php

@ -0,0 +1,19 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used during authentication for various
| messages that we need to display to the user. You are free to modify
| these language lines according to your application's requirements.
|
*/
'failed' => 'اطلاعات وارد شده صحیح نمی باشد',
'throttle' => 'درخواست بیش از حد مجاز! لطفا بعد از :seconds ثانیه دوباره امتحان کنید',
];

24
resources/lang/fa/notification.php

@ -0,0 +1,24 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Notification Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used by the notifications to build
| the simple message. You are free to change them to anything
| you want to customize your message to better match your application.
|
*/
'tags' => [
'create' => 'یک تگ ساخته شد.'
],
'business_user' => [
'create' => 'کاربر :user به کسب و کار :business اضافه شد.'
]
];

19
resources/lang/fa/pagination.php

@ -0,0 +1,19 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Pagination Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used by the paginator library to build
| the simple pagination links. You are free to change them to anything
| you want to customize your views to better match your application.
|
*/
'previous' => '&laquo; قبلی',
'next' => 'بعدی &raquo;',
];

22
resources/lang/fa/passwords.php

@ -0,0 +1,22 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Password Reset Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are the default lines which match reasons
| that are given by the password broker for a password update attempt
| has failed, such as for an invalid token or invalid new password.
|
*/
'reset' => 'رمز عبور شما با موفقیت بازیابی شد',
'sent' => 'ایمیلی برای بازیابی رمزعبور برای شما ارسال شد',
'throttled' => 'لطفا اندکی صبر کنید',
'token' => 'مشخصه بازیابی رمزعبور شما صحیح نمی باشد',
'user' => "کاربری با این اطلاعات وجود ندارد",
];

189
resources/lang/fa/validation.php

@ -0,0 +1,189 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Validation Language Lines
|--------------------------------------------------------------------------
|
| The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such
| as the size rules. Feel free to tweak each of these messages here.
|
*/
'accepted' => 'گزینه :attribute باید تایید شود',
'active_url' => 'گزینه :attribute یک آدرس سایت معتبر نیست',
'after' => 'گزینه :attribute باید تاریخی بعد از :date باشد',
'after_or_equal' => 'گزینه :attribute باید تاریخی مساوی یا بعد از :date باشد',
'alpha' => 'گزینه :attribute باید تنها شامل حروف باشد',
'alpha_dash' => 'گزینه :attribute باید تنها شامل حروف، اعداد، خط تیره و زیر خط باشد',
'alpha_num' => 'گزینه :attribute باید تنها شامل حروف و اعداد باشد',
'array' => 'گزینه :attribute باید آرایه باشد',
'before' => 'گزینه :attribute باید تاریخی قبل از :date باشد',
'before_or_equal' => 'گزینه :attribute باید تاریخی مساوی یا قبل از :date باشد',
'between' => [
'numeric' => 'گزینه :attribute باید بین :min و :max باشد',
'file' => 'گزینه :attribute باید بین :min و :max کیلوبایت باشد',
'string' => 'گزینه :attribute باید بین :min و :max کاراکتر باشد',
'array' => 'گزینه :attribute باید بین :min و :max آیتم باشد',
],
'boolean' => 'گزینه :attribute تنها می تواند صحیح یا غلط باشد',
'confirmed' => 'تایید مجدد گزینه :attribute صحیح نمی باشد',
'date' => 'گزینه :attribute یک تاریخ صحیح نمی باشد',
'date_equals' => 'گزینه :attribute باید تاریخی مساوی با :date باشد',
'date_format' => 'گزینه :attribute با فرمت :format همخوانی ندارد',
'different' => 'گزینه :attribute و :other باید متفاوت باشند',
'digits' => 'گزینه :attribute باید :digits عدد باشد',
'digits_between' => 'گزینه :attribute باید بین :min و :max عدد باشد',
'dimensions' => 'ابعاد تصویر گزینه :attribute مجاز نمی باشد',
'distinct' => 'گزینه :attribute دارای افزونگی داده می باشد',
'email' => 'گزینه :attribute باید یک آدرس ایمیل صحیح باشد',
'ends_with' => 'گزینه :attribute باید با یکی از این مقادیر پایان یابد، :values',
'exists' => 'گزینه انتخاب شده :attribute صحیح نمی باشد',
'file' => 'گزینه :attribute باید یک فایل باشد',
'filled' => 'گزینه :attribute نمی تواند خالی باشد',
'gt' => [
'numeric' => 'گزینه :attribute باید بزرگتر از :value باشد',
'file' => 'گزینه :attribute باید بزرگتر از :value کیلوبایت باشد',
'string' => 'گزینه :attribute باید بزرگتر از :value کاراکتر باشد',
'array' => 'گزینه :attribute باید بیشتر از :value آیتم باشد',
],
'gte' => [
'numeric' => 'گزینه :attribute باید بزرگتر یا مساوی :value باشد',
'file' => 'گزینه :attribute باید بزرگتر یا مساوی :value کیلوبایت باشد',
'string' => 'گزینه :attribute باید بزرگتر یا مساوی :value کاراکتر باشد',
'array' => 'گزینه :attribute باید :value آیتم یا بیشتر داشته باشد',
],
'image' => 'گزینه :attribute باید از نوع تصویر باشد',
'in' => 'گزینه انتخابی :attribute صحیح نمی باشد',
'in_array' => 'گزینه :attribute در :other وجود ندارد',
'integer' => 'گزینه :attribute باید از نوع عددی باشد',
'ip' => 'گزینه :attribute باید آی پی آدرس باشد',
'ipv4' => 'گزینه :attribute باید آی پی آدرس ورژن 4 باشد',
'ipv6' => 'گزینه :attribute باید آی پی آدرس ورژن 6 باشد',
'json' => 'گزینه :attribute باید از نوع رشته جیسون باشد',
'lt' => [
'numeric' => 'گزینه :attribute باید کمتر از :value باشد',
'file' => 'گزینه :attribute باید کمتر از :value کیلوبایت باشد',
'string' => 'گزینه :attribute باید کمتر از :value کاراکتر باشد',
'array' => 'گزینه :attribute باید کمتر از :value آیتم داشته باشد',
],
'lte' => [
'numeric' => 'گزینه :attribute باید مساوی یا کمتر از :value باشد',
'file' => 'گزینه :attribute باید مساوی یا کمتر از :value کیلوبایت باشد',
'string' => 'گزینه :attribute باید مساوی یا کمتر از :value کاراکتر باشد',
'array' => 'گزینه :attribute نباید کمتر از :value آیتم داشته باشد',
],
'max' => [
'numeric' => 'گزینه :attribute نباید بزرگتر از :max باشد',
'file' => 'گزینه :attribute نباید بزرگتر از :max کیلوبایت باشد',
'string' => 'گزینه :attribute نباید بزرگتر از :max کاراکتر باشد',
'array' => 'گزینه :attribute نباید بیشتر از :max آیتم داشته باشد',
],
'mimes' => 'گزینه :attribute باید دارای یکی از این فرمت ها باشد: :values',
'mimetypes' => 'گزینه :attribute باید دارای یکی از این فرمت ها باشد: :values',
'min' => [
'numeric' => 'گزینه :attribute باید حداقل :min باشد',
'file' => 'گزینه :attribute باید حداقل :min کیلوبایت باشد',
'string' => 'گزینه :attribute باید حداقل :min کاراکتر باشد',
'array' => 'گزینه :attribute باید حداقل :min آیتم داشته باشد',
],
'not_in' => 'گزینه انتخابی :attribute صحیح نمی باشد',
'not_regex' => 'فرمت گزینه :attribute صحیح نمی باشد',
'numeric' => 'گزینه :attribute باید از نوع عددی باشد',
'password' => 'رمزعبور صحیح نمی باشد',
'present' => 'گزینه :attribute باید از نوع درصد باشد',
'regex' => 'فرمت گزینه :attribute صحیح نمی باشد',
'required' => 'تکمیل گزینه :attribute الزامی است',
'required_if' => 'تکمیل گزینه :attribute زمانی که :other دارای مقدار :value است الزامی می باشد',
'required_unless' => 'تکمیل گزینه :attribute الزامی می باشد مگر :other دارای مقدار :values باشد',
'required_with' => 'تکمیل گزینه :attribute زمانی که مقدار :values درصد است الزامی است',
'required_with_all' => 'تکمیل گزینه :attribute زمانی که مقادیر :values درصد است الزامی می باشد',
'required_without' => 'تکمیل گزینه :attribute زمانی که مقدار :values درصد نیست الزامی است',
'required_without_all' => 'تکمیل گزینه :attribute زمانی که هیچ کدام از مقادیر :values درصد نیست الزامی است',
'same' => 'گزینه های :attribute و :other باید یکی باشند',
'size' => [
'numeric' => 'گزینه :attribute باید :size باشد',
'file' => 'گزینه :attribute باید :size کیلوبایت باشد',
'string' => 'گزینه :attribute باید :size کاراکتر باشد',
'array' => 'گزینه :attribute باید شامل :size آیتم باشد',
],
'starts_with' => 'گزینه :attribute باید با یکی از این مقادیر شروع شود، :values',
'string' => 'گزینه :attribute باید تنها شامل حروف باشد',
'timezone' => 'گزینه :attribute باید از نوع منطقه زمانی صحیح باشد',
'unique' => 'این :attribute از قبل ثبت شده است',
'uploaded' => 'آپلود گزینه :attribute شکست خورد',
'url' => 'فرمت :attribute اشتباه است',
'uuid' => 'گزینه :attribute باید یک UUID صحیح باشد',
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
|--------------------------------------------------------------------------
|
| Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule.
|
*/
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
/*
|--------------------------------------------------------------------------
| Custom Validation Attributes
|--------------------------------------------------------------------------
|
| The following language lines are used to swap our attribute placeholder
| with something more reader friendly such as "E-Mail Address" instead
| of "email". This simply helps us make our message more expressive.
|
*/
'attributes' => [
'name' => 'نام',
'username' => 'نام کاربری',
'email' => 'ایمیل',
'first_name' => 'نام',
'last_name' => 'نام خانوادگی',
'password' => 'رمز عبور',
'password_confirmation' => 'تاییدیه رمز عبور',
'city' => 'شهر',
'state' => 'استان',
'country' => 'کشور',
'address' => 'آدرس',
'phone' => 'تلفن',
'mobile' => 'تلفن همراه',
'age' => 'سن',
'sex' => 'جنسیت',
'gender' => 'جنسیت',
'day' => 'روز',
'month' => 'ماه',
'year' => 'سال',
'hour' => 'ساعت',
'minute' => 'دقیقه',
'second' => 'ثانیه',
'title' => 'عنوان',
'text' => 'متن',
'content' => 'محتوا',
'description' => 'توضیحات',
'date' => 'تاریخ',
'time' => 'زمان',
'available' => 'موجود',
'type' => 'نوع',
'img' => 'تصویر',
'image' => 'تصویر',
'size' => 'اندازه',
'color' => 'رنگ',
'captcha' => 'کد امنیتی',
'price' => 'قیمت',
'pic' => 'تصویر',
'link' => 'لینک',
],
];
Loading…
Cancel
Save