diff --git a/app/Enums/business.php b/app/Enums/business.php new file mode 100644 index 0000000..5690874 --- /dev/null +++ b/app/Enums/business.php @@ -0,0 +1 @@ + [ 'user' => 100, 'file' => 200, ] ]; \ No newline at end of file diff --git a/app/Enums/comment.php b/app/Enums/comment.php new file mode 100644 index 0000000..e516e01 --- /dev/null +++ b/app/Enums/comment.php @@ -0,0 +1 @@ + [ 'reject' => [ 'id' => 10, 'label' => 'رد' ], 'approve' => [ 'id' => 20, 'label' => 'تایید' ], ] ]; \ No newline at end of file diff --git a/app/Enums/levels.php b/app/Enums/levels.php new file mode 100644 index 0000000..8d1b627 --- /dev/null +++ b/app/Enums/levels.php @@ -0,0 +1 @@ + [ 'id' => 4, 'name' => 'Owner', 'label' => 'صاحب' ], 'admin' => [ 'id' => 3, 'name' => 'Admin', 'label' => 'مدیر' ], 'colleague' => [ 'id' => 2, 'name' => 'Colleague', 'label' => 'همکار' ], 'guest' => [ 'id' => 1, 'name' => 'Guest', 'label' => 'مهمان' ], 'inactive' => [ 'id' => 0, 'name' => 'Inactive', 'label' => 'غیر فعال' ], ]; \ No newline at end of file diff --git a/app/Enums/log.php b/app/Enums/log.php new file mode 100644 index 0000000..a49f08d --- /dev/null +++ b/app/Enums/log.php @@ -0,0 +1 @@ + [ User::class => 10, Business::class => 20, Project::class => 30, Task::class => 40, Spenthour::class => 50, ], 'actions' => [ 'created' => 10, 'updated' => 20, 'deleted' => 30, 'restored' => 40, ], ]; \ No newline at end of file diff --git a/app/Enums/post.php b/app/Enums/post.php new file mode 100644 index 0000000..0e8190c --- /dev/null +++ b/app/Enums/post.php @@ -0,0 +1 @@ + [ 'draft' => [ 'id' => 10, 'label' => 'پیش نویس' ], 'review' => [ 'id' => 20, 'label' => 'در حال بررسی' ], 'published' => [ 'id' => 30, 'label' => 'منتشر' ], 'trashed' => [ 'id' => 40, 'label' => 'حذف' ], ] ]; \ No newline at end of file diff --git a/app/Enums/roles.php b/app/Enums/roles.php new file mode 100644 index 0000000..6dd5eda --- /dev/null +++ b/app/Enums/roles.php @@ -0,0 +1 @@ + [ 'id' => 0, 'label' => 'غیر فعال' ], 'guest' => [ 'id' => 1, 'label' => 'میهمان' ], 'Colleague' => [ 'id' => 2, 'label' => 'همکار' ], 'senior' => [ 'id' => 3, 'label' => 'معاون' ], 'manager' => [ 'id' => 4, 'label' => 'مدیر' ], ]; \ No newline at end of file diff --git a/app/Enums/service.php b/app/Enums/service.php new file mode 100644 index 0000000..39ab78f --- /dev/null +++ b/app/Enums/service.php @@ -0,0 +1 @@ + [ 'file' => [ 'orphanage' => [ 'id' => 'orphanage', 'label' => 'داده‌های موقت', ] ] ] ]; \ No newline at end of file diff --git a/app/Enums/status.php b/app/Enums/status.php new file mode 100644 index 0000000..8bd99c4 --- /dev/null +++ b/app/Enums/status.php @@ -0,0 +1 @@ + [ 'inactive' => [ 'id' => 0, 'label' => 'غیر فعال', 'name' => 'Inactive' ], 'active' => [ 'id' => 1, 'label' => 'فعال', 'name' => 'Active' ], 'close' => [ 'id' => 2, 'label' => 'بسته', 'name' => 'Close' ], 'done' => [ 'id' => 3, 'label' => 'انجام شده', 'name' => 'Done' ], ], ]; \ No newline at end of file diff --git a/app/Enums/tables.php b/app/Enums/tables.php new file mode 100644 index 0000000..95a3d03 --- /dev/null +++ b/app/Enums/tables.php @@ -0,0 +1,56 @@ + [ + 'id' => 10, + 'name' => 'Businesses' + ], + 'projects' => [ + 'id' => 20, + 'name' => 'Projects' + ], + 'sprints' => [ + 'id' => 30, + 'name' => 'Sprints' + ], + 'statuses' => [ + 'id' => 40, + 'name' => 'Statuses' + ], + 'systems' => [ + 'id' => 50, + 'name' => 'Systems' + ], + 'workflows' => [ + 'id' => 60, + 'name' => 'Workflows' + ], + 'tags' => [ + 'id' => 70, + 'name' => 'Tags' + ], + 'tasks' => [ + 'id' => 80, + 'name' => 'Tasks' + ], + 'works' => [ + 'id' => 90, + 'name' => 'Works' + ], + + //Relation Table's + 'business_user' => [ + 'id' => 210, + 'name' => 'BusinessUser' + ], + 'project_user' => [ + 'id' => 220, + 'name' => 'ProjectUser' + ], + 'tag_task' => [ + 'id' => 230, + 'name' => 'TagTask' + ], +]; diff --git a/app/Enums/ticket.php b/app/Enums/ticket.php new file mode 100644 index 0000000..5899fbf --- /dev/null +++ b/app/Enums/ticket.php @@ -0,0 +1 @@ + [ 'sale' => [ 'id' => 1, 'label' => 'فروش', ], 'support' => [ 'id' => 2, 'label' => 'پشتیبانی' ] ], 'category' => [ 'webdesign-sale' => [ 'id' => 1, 'label' => 'طراحی سایت', 'type' => 1 ], 'seo-sale' => [ 'id' => 2, 'label' => 'فروش سئو', 'type' => 1 ], 'webdesign-support' => [ 'id' => 3, 'label' => 'پشتیبانی طراحی سایت', 'type' => 2 ] ], 'status' => [ 'active' => [ 'id' => 1, 'label' => 'فعال' ], 'close' => [ 'id' => 2, 'label' => 'بسته' ] ] ]; \ No newline at end of file diff --git a/app/Enums/user.php b/app/Enums/user.php new file mode 100644 index 0000000..e639578 --- /dev/null +++ b/app/Enums/user.php @@ -0,0 +1 @@ + [ 'guest' => [ 'id' => 1, 'label' => 'مهمان' ], 'user' => [ 'id' => 2, 'label' => 'کاربر' ], 'service' => [ 'id' => 3, 'label' => 'سرویس' ] ], 'status' => [ 'desable' => [ 'id' => 0, 'label' => 'غیر فعال' ], 'active' => [ 'id' => 1, 'label' => 'فعال' ] ], 'permissions' => [ // user 'user-user-create', 'user-user-view-any', 'user-user-update-own', 'user-user-update-any', 'user-user-role-own', 'user-user-role-any', 'user-role-create', 'user-role-view-any', // ticket 'ticket-ticket-create', 'ticket-ticket-view-any', 'ticket-ticket-reply-any', // post 'post-post-create', 'post-post-view-publish', 'post-post-view-any', 'post-post-view-own', 'post-post-update-own', 'post-post-update-any', 'post-post-delete-own', 'post-post-delete-any', // tag 'post-tag-update-any', 'post-tag-create', 'post-tag-delete-any', // taxonomies 'post-taxonomy-view-any', 'post-taxonomy-update-any', 'post-taxonomy-create', 'post-taxonomy-delete-any', // comments 'post-comment-view-any', 'post-comment-view-published', 'post-comment-view-own', 'post-comment-create', 'post-comment-update-any', 'post-comment-delete-any', ] ]; \ No newline at end of file diff --git a/app/Utilities/Exceptions/Handler.php b/app/Utilities/Exceptions/Handler.php new file mode 100644 index 0000000..79c1dec --- /dev/null +++ b/app/Utilities/Exceptions/Handler.php @@ -0,0 +1,58 @@ +getMethods( + ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED + ); + + + $result = []; + foreach ($methods as $method) { + // invoke the method so we can collect the result of execution it + $result[$method->name] = $method->invoke($exception); + } + + // Clear the unnecessary method + unset($result['getTrace']); + unset($result['__toString']); + + // clear the null values then encode it as json + // so we can decode it as an object in the Monolog Processor + $result = json_encode(array_filter($result)); + + return Log::emergency($result); + } + + public function render($request, Throwable $exception) + { + return parent::render($request, $exception); + } +} diff --git a/app/Utilities/Helpers/enum.php b/app/Utilities/Helpers/enum.php new file mode 100644 index 0000000..84fcfe8 --- /dev/null +++ b/app/Utilities/Helpers/enum.php @@ -0,0 +1 @@ +contains(".") ? "" : "."; // the first parameter of all enum keys are its filename [$filename, $key] = explode(".", $key, 2); // because we do not want to load the file every time use require $enums = require __DIR__ . "/../Enums/$filename.php"; // if the key that user provided not exists then null return $enums = Arr::get($enums, $key, null); // if enum null means that key not found throw_if($enums === null, 'Exception', "Undefined enum '{$key}'"); // if enum value is array its mean that user want to use it as collection return is_array($enums) ? collect($enums) : $enums; } } \ No newline at end of file diff --git a/app/Utilities/Helpers/http.php b/app/Utilities/Helpers/http.php new file mode 100644 index 0000000..bba7252 --- /dev/null +++ b/app/Utilities/Helpers/http.php @@ -0,0 +1 @@ +attach($piece->getFilename(), $piece); } } // if queue set then queue this command if ($queue !== null) { return dispatch(new AsyncCall(...func_get_args())); } try { // otherwise execute the pending request return $pendingRequest ->withToken($token) ->withoutRedirecting() ->withoutVerifying() ->$method($path, $data); } catch (Throwable $thr) { return $thr->response; } } } \ No newline at end of file diff --git a/app/Utilities/Helpers/index.php b/app/Utilities/Helpers/index.php new file mode 100644 index 0000000..64aade6 --- /dev/null +++ b/app/Utilities/Helpers/index.php @@ -0,0 +1 @@ +trim("."); if ($filename->isEmpty()) { continue; } require_once (string) $filename; } \ No newline at end of file diff --git a/app/Utilities/Helpers/permission.php b/app/Utilities/Helpers/permission.php new file mode 100644 index 0000000..a0e47ef --- /dev/null +++ b/app/Utilities/Helpers/permission.php @@ -0,0 +1,244 @@ +user()->id]['owner'] == true; + } +} + +/** + * Business Permit + */ + +if (!function_exists('businessAccess')) { + function businessAccess($ids) + { + return isset(request('_business_info')['info']['users'][$ids['user_id']?? auth()->id()]); + } +} + +if (!function_exists('isBusinessOwner')) { + function isBusinessOwner($ids) + { + return businessAccess($ids) && request('_business_info')['info']['users'][$ids['user_id'] ?? auth()->id()]['level'] == enum('levels.owner.id'); + } +} + +if (!function_exists('isAtLeastBusinessAdmin')) { + function isAtLeastBusinessAdmin($ids) + { + return businessAccess($ids) && request('_business_info')['info']['users'][$ids['user_id'] ?? auth()->id()]['level'] >= enum('levels.admin.id'); + } +} + +if (!function_exists('businessEdit')) { + function businessEdit($ids) + { + return isBusinessOwner($ids); + } +} + +if (!function_exists('businessUsers')) { + function businessUsers($ids) + { + return isBusinessOwner($ids); + } +} + +if (!function_exists('businessWorkFlows')) { + function businessWorkFlows($ids) + { + return isAtLeastBusinessAdmin($ids); + } +} + +if (!function_exists('businessProjects')) { + function businessProjects($ids) + { + return isBusinessOwner($ids); + } +} + +if (!function_exists('businessTags')) { + function businessTags($ids) + { + return isAtLeastBusinessAdmin($ids); + } +} + +if (!function_exists('businessStatuses')) { + function businessStatuses($ids) + { + return isAtLeastBusinessAdmin($ids); + } +} + +/** + * Project Permit + */ + +if (!function_exists('isInProject')) { + function isInProject($ids) + { + return isset(request('_business_info')['info']['projects'][$ids['project_id']]['members'][$ids['user_id'] ?? auth()->id()]); + } +} + +if (!function_exists('isActiveInProject')) { + function isActiveInProject($ids) + { + return request('_business_info')['info']['projects'][$ids['project_id']]['members'][$ids['user_id'] ?? auth()->id()]['level'] > 0; + } +} + +if (!function_exists('projectAccess')) { + function projectAccess($ids) + { + return isInProject($ids) && isActiveInProject($ids); + } +} + +if (!function_exists('isOwnerLevelInProject')) { + function isOwnerLevelInProject($ids) + { + return request('_business_info')['info']['projects'][$ids['project_id']]['members'][$ids['user_id'] ?? auth()->id()]['level'] == enum('levels.owner.id'); + } +} + +if (!function_exists('isAtLeastAdminLevelInProject')) { + function isAtLeastAdminLevelInProject($ids) + { + return request('_business_info')['info']['projects'][$ids['project_id']]['members'][$ids['user_id'] ?? auth()->id()]['level'] >= enum('levels.admin.id'); + } +} + +if (!function_exists('isAtLeastColleagueLevelInProject')) { + function isAtLeastColleagueLevelInProject($ids) + { + return request('_business_info')['info']['projects'][$ids['project_id']]['members'][$ids['user_id'] ?? auth()->id()]['level'] >= enum('levels.colleague.id'); + } +} + +if (!function_exists('isAtLeastGuestLevelInProject')) { + function isAtLeastGuestLevelInProject($ids) + { + return request('_business_info')['info']['projects'][$ids['project_id']]['members'][$ids['user_id'] ?? auth()->id()]['level'] >= enum('levels.guest.id'); + } +} + +if (!function_exists('isDefiniteGuestInProject')) { + function isDefiniteGuestInProject($ids) + { + return isInProject($ids) && request('_business_info')['info']['projects'][$ids['project_id']]['members'][$ids['user_id'] ?? auth()->id()]['level'] == enum('levels.guest.id'); + } +} + +if (!function_exists('isProjectOwner')) { + function isProjectOwner($ids) + { + return isInProject($ids) && isOwnerLevelInProject($ids); + } +} + +if (!function_exists('isProjectAdmin')) { + function isProjectAdmin($ids) + { + return isInProject($ids) && isAtLeastAdminLevelInProject($ids); + } +} + +if (!function_exists('isProjectColleague')) { + function isProjectColleague($ids) + { + return isInProject($ids) && isAtLeastColleagueLevelInProject($ids); + } +} + +if (!function_exists('isProjectGuest')) { + function isProjectGuest($ids) + { + return isInProject($ids) && isAtLeastGuestLevelInProject($ids); + } +} + +if (!function_exists('projectEdit')) { + function projectEdit($ids) + { + return isProjectAdmin($ids); + } +} + +if (!function_exists('projectUsers')) { + function projectUsers($ids) + { + return isProjectOwner($ids); + } +} + +if (!function_exists('projectSystems')) { + function projectSystems($ids) + { + return isProjectAdmin($ids); + } +} + +if (!function_exists('projectTasks')) { + function projectTasks($ids) + { + return isProjectAdmin($ids); + } +} + +if (!function_exists('projectSprints')) { + function projectSprints($ids) + { + return isProjectAdmin($ids); + } +} + +/** + * other + */ + +if (!function_exists('isActiveUser')) { + function isActiveUser($ids) + { + return businessAccess($ids) && request('_business_info')['info']['users'][$ids['user_id'] ?? auth()->id()]['level'] > enum('levels.inactive.id'); + } +} + diff --git a/app/Utilities/Jobs/AsyncCall.php b/app/Utilities/Jobs/AsyncCall.php new file mode 100644 index 0000000..9f03b1d --- /dev/null +++ b/app/Utilities/Jobs/AsyncCall.php @@ -0,0 +1 @@ +method = $method; $this->service = $service; $this->path = $path; $this->data = $data; $this->onQueue($queue); $this->onConnection('database'); } /** * Execute the job. * * @return void */ public function handle() { call($this->method, $this->service, $this->path, $this->data); } } \ No newline at end of file diff --git a/app/Utilities/Logger/CreateCustomLogger.php b/app/Utilities/Logger/CreateCustomLogger.php new file mode 100644 index 0000000..53898b6 --- /dev/null +++ b/app/Utilities/Logger/CreateCustomLogger.php @@ -0,0 +1,83 @@ + Monolog::DEBUG, + 'info' => Monolog::INFO, + 'notice' => Monolog::NOTICE, + 'warning' => Monolog::WARNING, + 'error' => Monolog::ERROR, + 'critical' => Monolog::CRITICAL, + 'alert' => Monolog::ALERT, + 'emergency' => Monolog::EMERGENCY, + ]; + + public function __invoke(array $config) + { + if (!is_a($config['handler'], HandlerInterface::class, true)) { + throw new InvalidArgumentException( + $config['handler'] . ' must be an instance of ' . HandlerInterface::class + ); + } + + $with = array_merge( + ['level' => $this->level($config)], + $config['with'] ?? [], + $config['handler_with'] ?? [] + ); + + $handlers = [ + $this->prepareHandler(app()->make($config['handler'], $with), $config) + ]; + + $processors = [ + new LogServiceRecordProcessor, + ]; + + return new Monolog('custom', $handlers, $processors, new DateTimeZone('Asia/Tehran')); + } + + protected function getFallbackChannelName() + { + return app()->bound('env') ? app()->environment() : 'production'; + } + + protected function prepareHandler(HandlerInterface $handler, array $config = []) + { + $isHandlerFormattable = false; + + if (Monolog::API === 1) { + $isHandlerFormattable = true; + } elseif (Monolog::API === 2 && $handler instanceof FormattableHandlerInterface) { + $isHandlerFormattable = true; + } + + if ($isHandlerFormattable && !isset($config['formatter'])) { + $handler->setFormatter($this->formatter()); + } elseif ($isHandlerFormattable && $config['formatter'] !== 'default') { + $handler->setFormatter(app()->make($config['formatter'], $config['formatter_with'] ?? [])); + } + + return $handler; + } + + protected function level(array $config) + { + $level = $config['level'] ?? 'debug'; + + if (isset($this->levels[$level])) { + return $this->levels[$level]; + } + + throw new InvalidArgumentException('Invalid log level.'); + } +} diff --git a/app/Utilities/Logger/LogServiceRecordProcessor.php b/app/Utilities/Logger/LogServiceRecordProcessor.php new file mode 100644 index 0000000..c354dfc --- /dev/null +++ b/app/Utilities/Logger/LogServiceRecordProcessor.php @@ -0,0 +1,37 @@ + Uuid::uuid4()->toString(), + 'breadcrumbs' => DB::getQueryLog(), + 'request' => app('request'), + 'message' => Arr::get($exception, 'getMessage', $record['message']), + 'from' => env('CONTAINER_NAME'), + 'trace' => Arr::get($exception, 'getTraceAsString'), + 'name' => Arr::get($exception, 'getName'), + 'code' => Arr::get($exception, 'getCode'), + 'file' => Arr::get($exception, 'getFile'), + 'line' => Arr::get($exception, 'getLine'), + 'user_id' => Auth::hasResolvedGuards() ? Auth::id() : null, + 'created_at' => Carbon::now(), + ]); + } catch (Throwable $exception) { + return $record; + } + } +} diff --git a/app/Utilities/Middlewares/BindBusinessInfo.php b/app/Utilities/Middlewares/BindBusinessInfo.php new file mode 100644 index 0000000..04fbb6f --- /dev/null +++ b/app/Utilities/Middlewares/BindBusinessInfo.php @@ -0,0 +1 @@ +has('business_id') ? $request->business_id : $request->route('business'); $businessInfo = env('CONTAINER_NAME') == 'hi-user-app' ? \App\Business::info($business_id) : get('user', env('USER_URL') . 'actions/businesses/' . $business_id . '/info', []); $request->merge(['_business_info' => $businessInfo]); return $next($request); } } \ No newline at end of file diff --git a/app/Utilities/Models/Model.php b/app/Utilities/Models/Model.php new file mode 100644 index 0000000..6c66291 --- /dev/null +++ b/app/Utilities/Models/Model.php @@ -0,0 +1,254 @@ +action = static::CREATED; + }); + + static::updated(function ($model) { + $model->action = static::UPDATED; + }); + + static::deleted(function ($model) { + $model->action = static::DELETED; + }); + } + + /** + * @return void + * @throw \Exception + */ + public function rules() + { + return []; + } + + /** + * + * + * @param array $attributes + * @return void + */ + public function validate(array $attributes = null) + { + $attributes = $attributes ?? $this->getAttributes(); + + /** @var Validator $validator */ + $validator = app('validator')->make($attributes, $this->rules()); + + if ($validator->fails()) { + throw new ValidationException( + $validator, + new JsonResponse($validator->errors()->getMessages(), Response::HTTP_UNPROCESSABLE_ENTITY) + ); + } + } + + /** + * @return void + */ + public function updateRelations() + { + } + + /** + * @param string|null $key + * @return void + */ + public function getValueOf(?string $key) + { + $values = []; + + if ($key && isset($values, $key)) { + return $values[$key]; + } + + return $values; + } + + protected function makeChanges() + { + if (empty($this->reportable)) { + return; + } + + $changes = new Collection($this->getDirty()); + + // fillable * or field + $changes = $changes->filter(function ($value, $key) { + foreach ($this->reportable as $i => $name) { + if ($key === $name) { + return true; + } + } + return false; + }); + + if (($changes->isEmpty() && $this->action == static::UPDATED)) { + return; + } + + return [ + 'original' => $this->getOriginal() + $this->getAttributes(), + 'diff' => $changes->toArray(), + ]; + +// return [ +// 'auth' => Auth::id(), +// 'timestamp' => $this->freshTimestamp(), +// 'business' => $this->getValueOf('business_id'), +// 'info' => \request('_business_info')['info'] ?? null, +// 'project' => $this->getValueOf('project_id'), +// 'data' => [ +// 'sprint_id' => $this->getValueOf('sprint_id'), +// 'system_id' => $this->getValueOf('system_id'), +// 'workflow_id' => $this->getValueOf('workflow_id'), +// 'status_id' => $this->getValueOf('status_id'), +// 'user_id' => $this->getValueOf('user_id'), +// 'table_name' => $this->getTable(), +// 'crud_id' => $this->action, +// 'original' => $this->getOriginal() + $this->getAttributes(), +// 'diff' => $changes->toArray(), +// ], +// 'from' => env('CONTAINER_NAME'), +// ]; + } + + protected function report($changes): void + { + if ($this->action == null){ + return; + } + $payload = [ + 'auth' => Auth::id(), + 'timestamp' => $this->freshTimestamp(), + 'business' => $this->getValueOf('business_id'), + 'info' => \request('_business_info') ?? null, + 'project' => $this->getValueOf('project_id'), + 'data' => [ + 'sprint_id' => $this->getValueOf('sprint_id'), + 'system_id' => $this->getValueOf('system_id'), + 'workflow_id' => $this->getValueOf('workflow_id'), + 'status_id' => $this->getValueOf('status_id'), + 'task_id' => $this->getValueOf('task_id'), + 'subject_id' => $this->getValueOf('subject_id'), + 'user_id' => $this->getValueOf('user_id'), + 'table_name' => $this->getTable(), + 'crud_id' => $this->action, + 'original' => $changes['original'] + $this->getOriginal(), + 'diff' => $changes['diff'], + ], + '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, ""); + } + } + + /** + * @param array $options + * @return void + */ + public function save(array $options = []) + { + // The validation function is called first + $this->validate(); + + // Then, because the relationships are set as attributes in this model + // we pre-enter their names in filled_relation attribute and store + // them in a temporary variable with a loop. + foreach ($this->fillable_relations as $relation) { + $this->filled_relations[$relation] = $this[$relation]; + unset($this[$relation]); + } + + // all of its action inside one transaction + // so if any of them failed the whole + // process rollbacked + DB::transaction(function () use ($options) { + // report to the activity aggregator + $changes = $this->makeChanges(); + + // save the model with it's attributes + parent::save($options); + + // save the model with it's relationships + $this->updateRelations(); + + is_array($changes) ? $this->report($changes) : true; + }, 3); + } + + public function delete() + { + $changes = $this->makeChanges(); + parent::delete(); + is_array($changes) ? $this->report($changes) : true; + } +} diff --git a/app/Utilities/Models/ReportableRelation.php b/app/Utilities/Models/ReportableRelation.php new file mode 100644 index 0000000..52f96d3 --- /dev/null +++ b/app/Utilities/Models/ReportableRelation.php @@ -0,0 +1,138 @@ +action = static::CREATED; + static::report($model); + }); + static::updated(function ($model) { + $model->action = static::UPDATED; + static::report($model); + }); + static::deleted(function ($model) { + $model->action = static::DELETED; + static::report($model); + }); + } + + public static function report($model) + { + $payload = [ + 'auth' => Auth::id(), + 'timestamp' => $model->freshTimestamp(), + 'business' => $model->pivotParent->getValueOf('business_id'), + 'info' => \request('_business_info') ?? null, + 'project' => $model->pivotParent->getValueOf('project_id'), + 'data' => [ + 'sprint_id' => $model->pivotParent->getValueOf('sprint_id'), + 'system_id' => $model->pivotParent->getValueOf('system_id'), + 'workflow_id' => $model->pivotParent->getValueOf('workflow_id'), + 'status_id' => $model->pivotParent->getValueOf('status_id'), + 'task_id' => $model->pivotParent->getValueOf('task_id'), + 'user_id' => $model->user_id, + 'table_name' => $model->getTable(), + 'crud_id' => $model->action, + 'original' => $model->getOriginal() + $model->getAttributes(), + 'diff' => $model->getChanges(), + ], + '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, ""); + } + } + + public function properties() + { + return $properties = [ + // Message properties + 'Persistent' => '1', // Marks a message as persistent + + // those familiar with the protocol may choose to use this property instead of Persistent. They control the same thing. + // Non-persistent (1) or persistent (2). + 'DeliveryMode' => '', + + // Used to describe the mime-type of the encoding. For example for the often used JSON encoding it is a good practice to set this property to: application/json. + // MIME content type. + // short string (max. 256 characters) + 'content_type' => '', + + // Commonly used to name a callback queue. + // Address to reply to. + // short string (max. 256 characters) + 'reply_to' => '', + + // Useful to correlate RPC responses with requests. + // Application correlation identifier. + // short string (max. 256 characters) + 'correlation_id' => '', + + /** Rarley Used Properties */ + + // This defines the message priority + // Message priority, 0 to 9 + 'priority' => '', + + // This is the message time stamp + // Message timestamp. + 'timestamp' => '', + + // This is the broker with whom the user sends the message (by default, it is "guest"). + // Creating user id. + // short string (max. 256 characters) + 'user_id' => '', + + // MIME content encoding. + // short string (max. 256 characters) + 'content_encoding' => '', + + // Message expiration specification. + // short string (max. 256 characters) + 'expiration' => '', + + // Application message identifier. + // short string (max. 256 characters) + 'message_id' => '', + + // Message type name. + // short string (max. 256 characters) + 'type' => '', + + // Creating application id. + // short string (max. 256 characters) + 'app_id' => '', + 'cluster_id' => '', + ]; + } +} diff --git a/app/Utilities/Providers/AuthServiceProvider.php b/app/Utilities/Providers/AuthServiceProvider.php new file mode 100644 index 0000000..d64ae2d --- /dev/null +++ b/app/Utilities/Providers/AuthServiceProvider.php @@ -0,0 +1 @@ +app['auth']->viaRequest('api', function (Request $request) { // with token if ($request->bearerToken()) { return $this->loginUserWithToken($request); } // first party if ($request->getHost() === $container_name = getenv('CONTAINER_NAME')) { return $this->loginServiceWithSetUser($container_name); } // without token return null; }); } public function loginUserWithToken(Request $request): User { $attributes = Http::retry(3, 100) ->withToken($request->bearerToken()) ->get(env('USER_URL') . 'auth') ->json(); $attributes = Arr::get($attributes, 'data',[]); // dd($attributes); return new User($attributes); } public function loginServiceWithSetUser(string $container_name): User { Auth::setUser($user = new User([ 'name' => $container_name, 'is_service' => true, ])); return $user; } } \ No newline at end of file diff --git a/app/Utilities/Providers/HiLibraryServiceProvider.php b/app/Utilities/Providers/HiLibraryServiceProvider.php new file mode 100644 index 0000000..6c8ac8b --- /dev/null +++ b/app/Utilities/Providers/HiLibraryServiceProvider.php @@ -0,0 +1,49 @@ +app->singleton( + \Illuminate\Contracts\Debug\ExceptionHandler::class, + \App\HiLib\Exceptions\Handler::class + ); + + Stringable::macro('then', function($callback) { + return new Stringable($callback($this)); + }); + } + + /** + * Bootstrap any application services. + * + * @return void + */ + public function boot() + { + DB::enableQueryLog(); + } + + /** + * Get the services provided by the provider. + * + * @return array + */ + public function provides() + { + // + } +} diff --git a/config/amqp.php b/config/amqp.php index d0734cf..1b1f582 100644 --- a/config/amqp.php +++ b/config/amqp.php @@ -11,7 +11,7 @@ return [ 'rabbitmq' => [ 'connection' => [ - 'host' => env('AMQP_HOST', 'base-rabbitmq'), + 'host' => env('AMQP_HOST', 'liwo_redis_1'), 'port' => env('AMQP_PORT', 5672), 'username' => env('AMQP_USERNAME', 'root'), 'password' => env('AMQP_PASSWORD', 'root'), diff --git a/config/logging.php b/config/logging.php index 31b0dcf..9013913 100644 --- a/config/logging.php +++ b/config/logging.php @@ -16,7 +16,7 @@ return [ 'handler' => AmqpHandler::class, 'with' => [ 'exchange' => new AMQPChannel( - new AMQPStreamConnection("base-rabbitmq", 5672, 'root', 'root') + new AMQPStreamConnection("liwo_redis_1", 5672, 'root', 'root') ), 'exchangeName' => 'log_exchange' ], diff --git a/docker-compose.yml b/docker-compose.yml index 53c8d2c..46e149f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,39 @@ # For more information: https://laravel.com/docs/sail version: '3' services: + minio: + image: minio/minio + command: server /data + ports: + - 9000:9000 + environment: + MINIO_ACCESS_KEY: root + MINIO_SECRET_KEY: minioroot + networks: + - sail + commander: + image: rediscommander/redis-commander:latest + environment: + - REDIS_HOST=redis + - REDIS_PORT=6379 + - HTTP_USER=root + - HTTP_PASSWORD=root + - REDIS_PASSWORD=root + ports: + - 8081:8081 + depends_on: + - redis + networks: + - sail + redis: + image: redis:latest + ports: + - 6379:6379 + command: redis-server --requirepass root + volumes: + - redis-data:/data + networks: + - sail laravel.test: build: context: ./vendor/laravel/sail/runtimes/8.0 @@ -51,5 +84,7 @@ networks: sail: driver: bridge volumes: + redis-data: + driver: local sailmysql: driver: local