Browse Source

Merge docker services with main docker compose file

pull/2/head
Mohammad Akbari 4 years ago
parent
commit
994c7d9b33
Signed by: akbarjimi GPG Key ID: 55726AEFECE5E683
  1. 1
      app/Enums/business.php
  2. 1
      app/Enums/comment.php
  3. 1
      app/Enums/levels.php
  4. 1
      app/Enums/log.php
  5. 1
      app/Enums/post.php
  6. 1
      app/Enums/roles.php
  7. 1
      app/Enums/service.php
  8. 1
      app/Enums/status.php
  9. 56
      app/Enums/tables.php
  10. 1
      app/Enums/ticket.php
  11. 1
      app/Enums/user.php
  12. 58
      app/Utilities/Exceptions/Handler.php
  13. 1
      app/Utilities/Helpers/enum.php
  14. 1
      app/Utilities/Helpers/http.php
  15. 1
      app/Utilities/Helpers/index.php
  16. 244
      app/Utilities/Helpers/permission.php
  17. 1
      app/Utilities/Jobs/AsyncCall.php
  18. 83
      app/Utilities/Logger/CreateCustomLogger.php
  19. 37
      app/Utilities/Logger/LogServiceRecordProcessor.php
  20. 1
      app/Utilities/Middlewares/BindBusinessInfo.php
  21. 254
      app/Utilities/Models/Model.php
  22. 138
      app/Utilities/Models/ReportableRelation.php
  23. 1
      app/Utilities/Providers/AuthServiceProvider.php
  24. 49
      app/Utilities/Providers/HiLibraryServiceProvider.php
  25. 2
      config/amqp.php
  26. 2
      config/logging.php
  27. 35
      docker-compose.yml

1
app/Enums/business.php

@ -0,0 +1 @@
<?php return [ 'fee' => [ 'user' => 100, 'file' => 200, ] ];

1
app/Enums/comment.php

@ -0,0 +1 @@
<?php return [ 'status' => [ 'reject' => [ 'id' => 10, 'label' => 'رد' ], 'approve' => [ 'id' => 20, 'label' => 'تایید' ], ] ];

1
app/Enums/levels.php

@ -0,0 +1 @@
<?php return [ 'owner' => [ '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' => 'غیر فعال' ], ];

1
app/Enums/log.php

@ -0,0 +1 @@
<?php use App\User; use App\Business; use App\Project; use App\Task; use App\SpentHour; return [ 'types' => [ User::class => 10, Business::class => 20, Project::class => 30, Task::class => 40, Spenthour::class => 50, ], 'actions' => [ 'created' => 10, 'updated' => 20, 'deleted' => 30, 'restored' => 40, ], ];

1
app/Enums/post.php

@ -0,0 +1 @@
<?php return [ 'status' => [ 'draft' => [ 'id' => 10, 'label' => 'پیش نویس' ], 'review' => [ 'id' => 20, 'label' => 'در حال بررسی' ], 'published' => [ 'id' => 30, 'label' => 'منتشر' ], 'trashed' => [ 'id' => 40, 'label' => 'حذف' ], ] ];

1
app/Enums/roles.php

@ -0,0 +1 @@
<?php return [ 'hidden' => [ 'id' => 0, 'label' => 'غیر فعال' ], 'guest' => [ 'id' => 1, 'label' => 'میهمان' ], 'Colleague' => [ 'id' => 2, 'label' => 'همکار' ], 'senior' => [ 'id' => 3, 'label' => 'معاون' ], 'manager' => [ 'id' => 4, 'label' => 'مدیر' ], ];

1
app/Enums/service.php

@ -0,0 +1 @@
<?php return [ 'post' => [ 'file' => [ 'orphanage' => [ 'id' => 'orphanage', 'label' => 'داده‌های موقت', ] ] ] ];

1
app/Enums/status.php

@ -0,0 +1 @@
<?php return [ 'states' => [ 'inactive' => [ 'id' => 0, 'label' => 'غیر فعال', 'name' => 'Inactive' ], 'active' => [ 'id' => 1, 'label' => 'فعال', 'name' => 'Active' ], 'close' => [ 'id' => 2, 'label' => 'بسته', 'name' => 'Close' ], 'done' => [ 'id' => 3, 'label' => 'انجام شده', 'name' => 'Done' ], ], ];

56
app/Enums/tables.php

@ -0,0 +1,56 @@
<?php
return [
//Main Table's
'businesses' => [
'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'
],
];

1
app/Enums/ticket.php

@ -0,0 +1 @@
<?php return [ 'type' => [ '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' => 'بسته' ] ] ];

1
app/Enums/user.php

@ -0,0 +1 @@
<?php return [ 'type' => [ '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', ] ];

58
app/Utilities/Exceptions/Handler.php

@ -0,0 +1,58 @@
<?php
namespace App\HiLib\Exceptions;
use Throwable;
use ReflectionClass;
use ReflectionMethod;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\ValidationException;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Laravel\Lumen\Exceptions\Handler as ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\HttpException;
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that should not be reported.
*
* @var array
*/
protected $dontReport = [
AuthorizationException::class,
HttpException::class,
ModelNotFoundException::class,
ValidationException::class,
];
public function report(Throwable $exception)
{
// A trick that I took from Laravel macroable trait
$methods = (new ReflectionClass($exception))->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);
}
}

1
app/Utilities/Helpers/enum.php

@ -0,0 +1 @@
<?php use Illuminate\Support\Arr; use Illuminate\Support\Str; if (! function_exists('enum')) { function enum($key) { // add a dot at the end of string to prevent undefined offset $key .= Str::of($key)->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; } }

1
app/Utilities/Helpers/http.php

@ -0,0 +1 @@
<?php use App\Jobs\AsyncCall; use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\Http; use Symfony\Component\HttpFoundation\Request; if (!function_exists('get')) { function get(string $service, string $path, array $data, ?string $queue = null) { return call(Request::METHOD_GET, $service, $path, $data, $queue); } } if (!function_exists('post')) { function post(string $service, string $path, array $data, ?string $queue = null) { return call(Request::METHOD_POST, $service, $path, $data, $queue); } } if (!function_exists('put')) { function put(string $service, string $path, array $data, ?string $queue = null) { return call(Request::METHOD_PUT, $service, $path, $data, $queue); } } if (!function_exists('delete')) { function delete(string $service, string $path, array $data, ?string $queue = null) { return call(Request::METHOD_DELETE, $service, $path, $data, $queue); } } if (!function_exists('call')) { /** * @return \Illuminate\Http\Client\Response */ function call(string $method, string $service, string $path, array $data, ?string $queue = null) { // token of this service for send data to other service $token = 'YT76Nt2ofTbmkiP0ubvnlwOJLBtglA3UubjRhieTiTVP7jGPNX0RlueVOgIc'; // url for reaching the target service $baseUrl = config("services.$service"); // create a pending request for this url $pendingRequest = Http::retry(3, 100); // if command data contain file, then it will be attached to the pending request foreach ($data as $piece) { if ($piece instanceof UploadedFile) { $pendingRequest->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; } } }

1
app/Utilities/Helpers/index.php

@ -0,0 +1 @@
<?php use Illuminate\Support\Str; $helpers = scandir(__DIR__); foreach ($helpers as $helper) { $filename = Str::of($helper)->trim("."); if ($filename->isEmpty()) { continue; } require_once (string) $filename; }

244
app/Utilities/Helpers/permission.php

@ -0,0 +1,244 @@
<?php
use Illuminate\Http\Response;
if (!function_exists('businessInfoIsEmpty')) {
function businessInfoIsEmpty($userId)
{
return !isset(request('_business_info')['members'][$userId]);
}
}
if (!function_exists('permit')) {
function permit($actionName = null, $ids = [])
{
throw_if($actionName === null, '$actionName must not be null.');
$response = call_user_func($actionName, $ids);
if ($response === false) {
abort(Response::HTTP_FORBIDDEN);
}
}
}
if (!function_exists('can')) {
function can($actionName = null, $ids = [])
{
throw_if($actionName === null, '$actionName must not be null.');
return call_user_func($actionName, $ids);
}
}
if (!function_exists('isOwner')) {
function isOwner()
{
return request('_business_info')['users'][auth()->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');
}
}

1
app/Utilities/Jobs/AsyncCall.php

@ -0,0 +1 @@
<?php namespace App\HiLib\Jobs; use App\Jobs\Job; class AsyncCall extends Job { private string $method; private string $service; private string $path; private array $data; /** * AsyncCall constructor. * @param string $method * @param string $service * @param string $path * @param array $data * @param string $queue */ public function __construct(string $method, string $service, string $path, array $data, string $queue) { $this->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); } }

83
app/Utilities/Logger/CreateCustomLogger.php

@ -0,0 +1,83 @@
<?php
namespace App\HiLib\Logger;
use DateTimeZone;
use InvalidArgumentException;
use Monolog\Logger as Monolog;
use Monolog\Handler\HandlerInterface;
use App\HiLib\Logger\LogServiceRecordProcessor;
class CreateCustomLogger
{
protected $levels = [
'debug' => 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.');
}
}

37
app/Utilities/Logger/LogServiceRecordProcessor.php

@ -0,0 +1,37 @@
<?php
namespace App\HiLib\Logger;
use Throwable;
use Carbon\Carbon;
use Ramsey\Uuid\Uuid;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class LogServiceRecordProcessor
{
public function __invoke($record)
{
try {
$exception = json_decode($record['message'], true);
return array_merge($record, [
'correlation_id' => 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;
}
}
}

1
app/Utilities/Middlewares/BindBusinessInfo.php

@ -0,0 +1 @@
<?php namespace App\HiLib\Middlewares; use Closure; class BindBusinessInfo { public function handle($request, Closure $next, $guard = null) { $business_id = $request->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); } }

254
app/Utilities/Models/Model.php

@ -0,0 +1,254 @@
<?php
namespace App\HiLib\Models;
use Anik\Amqp\Exchange;
use Anik\Amqp\Facades\Amqp;
use PhpAmqpLib\Wire\AMQPTable;
use Anik\Amqp\PublishableMessage;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\Validator;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpFoundation\Response;
use Illuminate\Database\Eloquent\Model as EloquentModel;
class Model extends EloquentModel
{
/**
* Introducing model relationships
*
* @var array
*/
protected $fillable_relations = [];
/**
* Models that are ready to change.
*
* @var array
*/
protected $filled_relations = [];
/**
* Models that are ready to change.
*
* @var array
*/
protected $reportable = [];
protected $dirties = [];
protected $action = null;
public const CREATED = 10;
public const UPDATED = 20;
public const DELETED = 30;
public const RESTORED = 40;
protected static function booted()
{
static::created(function ($model) {
$model->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;
}
}

138
app/Utilities/Models/ReportableRelation.php

@ -0,0 +1,138 @@
<?php
namespace App\HiLib\Models;
use Anik\Amqp\Exchange;
use Anik\Amqp\Facades\Amqp;
use PhpAmqpLib\Wire\AMQPTable;
use Anik\Amqp\PublishableMessage;
use Illuminate\Support\Facades\Auth;
use Illuminate\Database\Eloquent\Relations\Pivot;
class ReportableRelation extends Pivot
{
public const CREATED = 10;
public const UPDATED = 20;
public const DELETED = 30;
protected static function booted()
{
static::created(function ($model) {
$model->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' => '',
];
}
}

1
app/Utilities/Providers/AuthServiceProvider.php

@ -0,0 +1 @@
<?php namespace App\HiLib\Providers; use App\User; use Illuminate\Http\Request; use Illuminate\Support\Arr; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Http; use Illuminate\Support\ServiceProvider; class AuthServiceProvider extends ServiceProvider { public function boot() { $this->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; } }

49
app/Utilities/Providers/HiLibraryServiceProvider.php

@ -0,0 +1,49 @@
<?php
namespace App\HiLib\Providers;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Stringable;
use Illuminate\Support\ServiceProvider;
class HiLibraryServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* Within the register method, you should only bind things into the service container.
*
* @return void
*/
public function register()
{
$this->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()
{
//
}
}

2
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'),

2
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'
],

35
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
Loading…
Cancel
Save