diff --git a/app/DataTables/User/CodeDataTable.php b/app/DataTables/User/CodeDataTable.php index 33b033c..a14e61e 100644 --- a/app/DataTables/User/CodeDataTable.php +++ b/app/DataTables/User/CodeDataTable.php @@ -42,7 +42,7 @@ public function dataTable(QueryBuilder $query): EloquentDataTable public function query(Code $model): QueryBuilder { - return $model->newQuery()->where('user_id', auth()->user()->first()->id); + return $model->newQuery()->where('user_id', auth()->user()->id); } public function html(): HtmlBuilder @@ -61,6 +61,7 @@ public function getColumns(): array return [ Column::make('id')->title('ایدی'), Column::make('title')->title('عنوان'), + Column::make('user_id')->title('user'), Column::make('code')->title('کد'), Column::make('created_at')->title('ایجاد شده در'), Column::make('action')->title('کاربردی'), diff --git a/app/Http/Controllers/Admin/DashboardController.php b/app/Http/Controllers/Admin/DashboardController.php index 64fc713..4ee2e3a 100644 --- a/app/Http/Controllers/Admin/DashboardController.php +++ b/app/Http/Controllers/Admin/DashboardController.php @@ -3,16 +3,21 @@ namespace App\Http\Controllers\Admin; use App\Http\Controllers\Controller; +use App\Models\Code; +use App\Models\CodesVisit; +use App\Models\Email; use App\Models\User; +use App\Models\Visit; use Carbon\Carbon; +use Illuminate\Support\Facades\DB; class DashboardController extends Controller { public function index() { - function getUsers($day) + function GetTables($day, $model) { - $data = User::where('created_at', '>=', Carbon::now()->subDays($day)) + $data = $model::where('created_at', '>=', Carbon::now()->subDays($day)) ->selectRaw('DATE(created_at) as date, COUNT(*) as total') ->groupBy('date') ->orderBy('date', 'ASC') @@ -27,10 +32,29 @@ function getUsers($day) ]; } - $users_history = getUsers(10); + $users_history = GetTables(10, User::class); + $codes_history = GetTables(10, Code::class); + $codes_visits_history = GetTables(10, CodesVisit::class); + $emails_history = GetTables(10, Email::class); + + $best_code_visit = DB::table('codes_visits') + ->select('code_id', DB::raw('COUNT(*) as count')) + ->groupBy('code_id') + ->orderBy('count', 'DESC') + ->limit(10) + ->get(); + + $best_code_results = Code::whereIn('id', $best_code_visit->pluck('code_id')->toArray())->get(); + + $visits_history = GetTables(30, Visit::class); return view('admin.dashboard', [ 'users_history' => $users_history, + 'codes_history' => $codes_history, + 'codes_visits_history' => $codes_visits_history, + 'emails_history' => $emails_history, + 'best_code_results' => $best_code_results, + 'visits_history' => $visits_history, ]); } } diff --git a/app/Http/Controllers/Auth/EmailVerificationNotificationController.php b/app/Http/Controllers/Auth/EmailVerificationNotificationController.php index 044fc70..cf2b6e8 100644 --- a/app/Http/Controllers/Auth/EmailVerificationNotificationController.php +++ b/app/Http/Controllers/Auth/EmailVerificationNotificationController.php @@ -3,8 +3,10 @@ namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; +use App\Models\Email; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; +use Webklex\IMAP\Facades\Client; class EmailVerificationNotificationController extends Controller { @@ -19,6 +21,26 @@ public function store(Request $request): RedirectResponse $request->user()->sendEmailVerificationNotification(); + Email::create([ + 'to' => $request->user()->email, + 'title' => 'forgot_password', + ]); + + $client = Client::account('default'); + $client->connect(); + + $inbox = $client->getFolder('INBOX'); + $messages = $inbox->messages()->all()->get(); + + if ($messages->count() > 0) { + $message = $messages->last(); + if ($message = 'Mail delivery failed: returning message to sender') { + return back()->with('status', 'verification-link-fail'); + } + } else { + return back()->with('status', 'verification-link-fail'); + } + return back()->with('status', 'verification-link-sent'); } } diff --git a/app/Http/Controllers/VisitController.php b/app/Http/Controllers/VisitController.php new file mode 100644 index 0000000..bbecad1 --- /dev/null +++ b/app/Http/Controllers/VisitController.php @@ -0,0 +1,17 @@ + $request->ip(), + 'user_agent' => $request->userAgent(), + ]); + } +} diff --git a/app/Models/Email.php b/app/Models/Email.php new file mode 100644 index 0000000..9cab12c --- /dev/null +++ b/app/Models/Email.php @@ -0,0 +1,10 @@ +=5.0.0", + "php": ">=5.5.9", + "webklex/php-imap": "^4.1.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + }, + "laravel": { + "providers": [ + "Webklex\\IMAP\\Providers\\LaravelServiceProvider" + ], + "aliases": { + "Client": "Webklex\\IMAP\\Facades\\Client" + } + } + }, + "autoload": { + "psr-4": { + "Webklex\\IMAP\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Malte Goldenbaum", + "email": "github@webklex.com", + "role": "Developer" + } + ], + "description": "Laravel IMAP client", + "homepage": "https://github.com/webklex/laravel-imap", + "keywords": [ + "idle", + "imap", + "laravel", + "laravel-imap", + "mail", + "oauth", + "pop3", + "webklex" + ], + "support": { + "issues": "https://github.com/Webklex/laravel-imap/issues", + "source": "https://github.com/Webklex/laravel-imap/tree/4.1.2" + }, + "funding": [ + { + "url": "https://www.buymeacoffee.com/webklex", + "type": "custom" + }, + { + "url": "https://ko-fi.com/webklex", + "type": "ko_fi" + } + ], + "time": "2023-01-18T18:33:20+00:00" + }, + { + "name": "webklex/php-imap", + "version": "4.1.2", + "source": { + "type": "git", + "url": "https://github.com/Webklex/php-imap.git", + "reference": "94bf93ae8868ac1e073cfbaef377f0ca1acac2bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Webklex/php-imap/zipball/94bf93ae8868ac1e073cfbaef377f0ca1acac2bc", + "reference": "94bf93ae8868ac1e073cfbaef377f0ca1acac2bc", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "ext-iconv": "*", + "ext-json": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "illuminate/pagination": ">=5.0.0", + "nesbot/carbon": ">=1.0", + "php": ">=7.0.0", + "symfony/http-foundation": ">=2.8.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "symfony/mime": "Recomended for better extension support" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Webklex\\PHPIMAP\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Malte Goldenbaum", + "email": "github@webklex.com", + "role": "Developer" + } + ], + "description": "PHP IMAP client", + "homepage": "https://github.com/webklex/php-imap", + "keywords": [ + "imap", + "mail", + "php-imap", + "pop3", + "webklex" + ], + "support": { + "issues": "https://github.com/Webklex/php-imap/issues", + "source": "https://github.com/Webklex/php-imap/tree/4.1.2" + }, + "funding": [ + { + "url": "https://www.buymeacoffee.com/webklex", + "type": "custom" + }, + { + "url": "https://ko-fi.com/webklex", + "type": "ko_fi" + } + ], + "time": "2022-12-14T15:45:15+00:00" + }, { "name": "webmozart/assert", "version": "1.11.0", diff --git a/config/imap.php b/config/imap.php new file mode 100644 index 0000000..1fdd482 --- /dev/null +++ b/config/imap.php @@ -0,0 +1,226 @@ + env('IMAP_DEFAULT_ACCOUNT', 'default'), + + /* + |-------------------------------------------------------------------------- + | Default date format + |-------------------------------------------------------------------------- + | + | The default date format is used to convert any given Carbon::class object into a valid date string. + | These are currently known working formats: "d-M-Y", "d-M-y", "d M y" + | + */ + 'date_format' => 'd-M-Y', + + /* + |-------------------------------------------------------------------------- + | Available IMAP accounts + |-------------------------------------------------------------------------- + | + | Please list all IMAP accounts which you are planning to use within the + | array below. + | + */ + 'accounts' => [ + + 'default' => [// account identifier + 'host' => env('IMAP_HOST', ''), + 'port' => env('IMAP_PORT', ''), + 'protocol' => env('IMAP_PROTOCOL', 'imap'), + 'encryption' => env('IMAP_ENCRYPTION', 'ssl'), + 'validate_cert' => env('IMAP_VALIDATE_CERT', true), + 'username' => env('IMAP_USERNAME', ''), + 'password' => env('IMAP_PASSWORD', ''), + 'authentication' => env('IMAP_AUTHENTICATION', null), + 'proxy' => [ + 'socket' => null, + 'request_fulluri' => false, + 'username' => null, + 'password' => null, + ], + 'timeout' => 30, + 'extensions' => [], + ], + + /* + 'gmail' => [ // account identifier + 'host' => 'imap.gmail.com', + 'port' => 993, + 'encryption' => 'ssl', + 'validate_cert' => true, + 'username' => 'example@gmail.com', + 'password' => 'PASSWORD', + 'authentication' => 'oauth', + ], + + 'another' => [ // account identifier + 'host' => '', + 'port' => 993, + 'encryption' => false, + 'validate_cert' => true, + 'username' => '', + 'password' => '', + 'authentication' => null, + ] + */ + ], + + /* + |-------------------------------------------------------------------------- + | Available IMAP options + |-------------------------------------------------------------------------- + | + | Available php imap config parameters are listed below + | -Delimiter (optional): + | This option is only used when calling $oClient-> + | You can use any supported char such as ".", "/", (...) + | -Fetch option: + | IMAP::FT_UID - Message marked as read by fetching the body message + | IMAP::FT_PEEK - Fetch the message without setting the "seen" flag + | -Fetch sequence id: + | IMAP::ST_UID - Fetch message components using the message uid + | IMAP::ST_MSGN - Fetch message components using the message number + | -Body download option + | Default TRUE + | -Flag download option + | Default TRUE + | -Soft fail + | Default FALSE - Set to TRUE if you want to ignore certain exception while fetching bulk messages + | -RFC822 + | Default TRUE - Set to FALSE to prevent the usage of \imap_rfc822_parse_headers(). + | See https://github.com/Webklex/php-imap/issues/115 for more information. + | -Debug enable to trace communication traffic + | -UID cache enable the UID cache + | -Fallback date is used if the given message date could not be parsed + | -Boundary regex used to detect message boundaries. If you are having problems with empty messages, missing + | attachments or anything like this. Be advised that it likes to break which causes new problems.. + | -Message key identifier option + | You can choose between the following: + | 'id' - Use the MessageID as array key (default, might cause hickups with yahoo mail) + | 'number' - Use the message number as array key (isn't always unique and can cause some interesting behavior) + | 'list' - Use the message list number as array key (incrementing integer (does not always start at 0 or 1) + | 'uid' - Use the message uid as array key (isn't always unique and can cause some interesting behavior) + | -Fetch order + | 'asc' - Order all messages ascending (probably results in oldest first) + | 'desc' - Order all messages descending (probably results in newest first) + | -Disposition types potentially considered an attachment + | Default ['attachment', 'inline'] + | -Common folders + | Default folder locations and paths assumed if none is provided + | -Open IMAP options: + | DISABLE_AUTHENTICATOR - Disable authentication properties. + | Use 'GSSAPI' if you encounter the following + | error: "Kerberos error: No credentials cache + | file found (try running kinit) (...)" + | or ['GSSAPI','PLAIN'] if you are using outlook mail + | -Decoder options (currently only the message subject and attachment name decoder can be set) + | 'utf-8' - Uses imap_utf8($string) to decode a string + | 'mimeheader' - Uses mb_decode_mimeheader($string) to decode a string + | + */ + 'options' => [ + 'delimiter' => '/', + 'fetch' => \Webklex\PHPIMAP\IMAP::FT_PEEK, + 'sequence' => \Webklex\PHPIMAP\IMAP::ST_UID, + 'fetch_body' => true, + 'fetch_flags' => true, + 'soft_fail' => false, + 'rfc822' => true, + 'debug' => false, + 'uid_cache' => true, + // 'fallback_date' => "01.01.1970 00:00:00", + 'boundary' => '/boundary=(.*?(?=;)|(.*))/i', + 'message_key' => 'list', + 'fetch_order' => 'asc', + 'dispositions' => ['attachment', 'inline'], + 'common_folders' => [ + 'root' => 'INBOX', + 'junk' => 'INBOX/Junk', + 'draft' => 'INBOX/Drafts', + 'sent' => 'INBOX/Sent', + 'trash' => 'INBOX/Trash', + ], + 'decoder' => [ + 'message' => 'utf-8', // mimeheader + 'attachment' => 'utf-8', // mimeheader + ], + 'open' => [ + // 'DISABLE_AUTHENTICATOR' => 'GSSAPI' + ], + ], + + /* + |-------------------------------------------------------------------------- + | Available flags + |-------------------------------------------------------------------------- + | + | List all available / supported flags. Set to null to accept all given flags. + */ + 'flags' => ['recent', 'flagged', 'answered', 'deleted', 'seen', 'draft'], + + /* + |-------------------------------------------------------------------------- + | Available events + |-------------------------------------------------------------------------- + | + */ + 'events' => [ + 'message' => [ + 'new' => \Webklex\IMAP\Events\MessageNewEvent::class, + 'moved' => \Webklex\IMAP\Events\MessageMovedEvent::class, + 'copied' => \Webklex\IMAP\Events\MessageCopiedEvent::class, + 'deleted' => \Webklex\IMAP\Events\MessageDeletedEvent::class, + 'restored' => \Webklex\IMAP\Events\MessageRestoredEvent::class, + ], + 'folder' => [ + 'new' => \Webklex\IMAP\Events\FolderNewEvent::class, + 'moved' => \Webklex\IMAP\Events\FolderMovedEvent::class, + 'deleted' => \Webklex\IMAP\Events\FolderDeletedEvent::class, + ], + 'flag' => [ + 'new' => \Webklex\IMAP\Events\FlagNewEvent::class, + 'deleted' => \Webklex\IMAP\Events\FlagDeletedEvent::class, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Available masking options + |-------------------------------------------------------------------------- + | + | By using your own custom masks you can implement your own methods for + | a better and faster access and less code to write. + | + | Checkout the two examples custom_attachment_mask and custom_message_mask + | for a quick start. + | + | The provided masks below are used as the default masks. + */ + 'masks' => [ + 'message' => \Webklex\PHPIMAP\Support\Masks\MessageMask::class, + 'attachment' => \Webklex\PHPIMAP\Support\Masks\AttachmentMask::class, + ], +]; diff --git a/database/migrations/2024_09_16_093504_create_visits_table.php b/database/migrations/2024_09_16_093504_create_visits_table.php new file mode 100644 index 0000000..6a04cd1 --- /dev/null +++ b/database/migrations/2024_09_16_093504_create_visits_table.php @@ -0,0 +1,29 @@ +id(); + $table->string('user_ip'); + $table->string('user_agent'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('visits'); + } +}; diff --git a/database/migrations/2024_09_16_102554_create_emails_table.php b/database/migrations/2024_09_16_102554_create_emails_table.php new file mode 100644 index 0000000..07dad49 --- /dev/null +++ b/database/migrations/2024_09_16_102554_create_emails_table.php @@ -0,0 +1,29 @@ +id(); + $table->string('title'); + $table->string('to'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('emails'); + } +}; diff --git a/resources/views/admin/dashboard.blade.php b/resources/views/admin/dashboard.blade.php index c889723..501e891 100644 --- a/resources/views/admin/dashboard.blade.php +++ b/resources/views/admin/dashboard.blade.php @@ -1,6 +1,5 @@
-
@@ -11,9 +10,70 @@
+
+
+
+
تعداد کد ها
+

{{ array_sum($codes_history["totals"]) }}

+
+
+
+
+
+
+
+
تعداد بازدید از کد ها
+

{{ array_sum($codes_visits_history["totals"]) }}

+
+
+
+
+
+
+
+
تعداد ایمیل های ارسال شده
+

{{ array_sum($emails_history["totals"]) }}

+
+
+
+
+
+
+
+

پر بازدید ترین کد ها

+
+
+
+ @foreach($best_code_results as $best_code_result) +
+
+
+ {{ substr( $best_code_result["title"], 0, 2) }} +
+
+
+ {{ $best_code_result["title"] }} +
+
{{ $best_code_result->fa_created_at() }}
+
+
+
+ @endforeach +
+
+
+
+
+

بازدید سایت

+
+
+
+
+
+
-
+ @section("style") @vite(["resources/sass/app.scss"]) @endsection @@ -78,6 +138,246 @@ show: false, }, })).render(); + window.ApexCharts && (new ApexCharts(document.getElementById('small_chart_2'), { + chart: { + type: "area", + fontFamily: 'inherit', + height: 70.0, + sparkline: { + enabled: true + }, + animations: { + enabled: true + }, + }, + dataLabels: { + enabled: false, + }, + fill: { + opacity: .16, + type: 'solid' + }, + stroke: { + width: 2, + lineCap: "round", + curve: "smooth", + }, + series: [{ + name: "تعداد", + data: @json($codes_history["totals"]) + }], + tooltip: { + theme: 'dark' + }, + grid: { + strokeDashArray: 4, + }, + xaxis: { + labels: { + padding: 0, + }, + tooltip: { + enabled: false + }, + axisBorder: { + show: false, + }, + type: 'datetime', + }, + yaxis: { + labels: { + padding: 4 + }, + }, + labels: @json($codes_history["dates"]), + colors: ["#FF5C00"], + legend: { + show: false, + }, + })).render(); + window.ApexCharts && (new ApexCharts(document.getElementById('small_chart_3'), { + chart: { + type: "area", + fontFamily: 'inherit', + height: 70.0, + sparkline: { + enabled: true + }, + animations: { + enabled: true + }, + }, + dataLabels: { + enabled: false, + }, + fill: { + opacity: .16, + type: 'solid' + }, + stroke: { + width: 2, + lineCap: "round", + curve: "smooth", + }, + series: [{ + name: "تعداد", + data: @json($codes_visits_history["totals"]) + }], + tooltip: { + theme: 'dark' + }, + grid: { + strokeDashArray: 4, + }, + xaxis: { + labels: { + padding: 0, + }, + tooltip: { + enabled: false + }, + axisBorder: { + show: false, + }, + type: 'datetime', + }, + yaxis: { + labels: { + padding: 4 + }, + }, + labels: @json($codes_visits_history["dates"]), + colors: ["#FF5C00"], + legend: { + show: false, + }, + })).render(); + window.ApexCharts && (new ApexCharts(document.getElementById('small_chart_4'), { + chart: { + type: "area", + fontFamily: 'inherit', + height: 70.0, + sparkline: { + enabled: true + }, + animations: { + enabled: true + }, + }, + dataLabels: { + enabled: false, + }, + fill: { + opacity: .16, + type: 'solid' + }, + stroke: { + width: 2, + lineCap: "round", + curve: "smooth", + }, + series: [{ + name: "تعداد", + data: @json($emails_history["totals"]) + }], + tooltip: { + theme: 'dark' + }, + grid: { + strokeDashArray: 4, + }, + xaxis: { + labels: { + padding: 0, + }, + tooltip: { + enabled: false + }, + axisBorder: { + show: false, + }, + type: 'datetime', + }, + yaxis: { + labels: { + padding: 4 + }, + }, + labels: @json($emails_history["dates"]), + colors: ["#FF5C00"], + legend: { + show: false, + }, + })).render(); + window.ApexCharts && (new ApexCharts(document.getElementById('chart-mentions'), { + chart: { + type: "bar", + fontFamily: 'inherit', + height: 300, + parentHeightOffset: 0, + toolbar: { + show: false, + }, + animations: { + enabled: false + }, + stacked: true, + }, + plotOptions: { + bar: { + columnWidth: '50%', + } + }, + dataLabels: { + enabled: false, + }, + fill: { + opacity: 1, + }, + series: [{ + name: "Web", + data: @json($visits_history["totals"]) + }], + tooltip: { + theme: 'dark' + }, + grid: { + padding: { + top: -20, + right: 0, + left: -4, + bottom: -4 + }, + strokeDashArray: 4, + xaxis: { + lines: { + show: true + } + }, + }, + xaxis: { + labels: { + padding: 0, + }, + tooltip: { + enabled: false + }, + axisBorder: { + show: false, + }, + type: 'datetime', + }, + yaxis: { + labels: { + padding: 4 + }, + }, + labels: @json($visits_history["dates"]), + colors: ["#FF5C00"], + legend: { + show: false, + }, + })).render(); }); @endsection diff --git a/resources/views/auth/verify-email.blade.php b/resources/views/auth/verify-email.blade.php index 9b8340d..4e49dae 100644 --- a/resources/views/auth/verify-email.blade.php +++ b/resources/views/auth/verify-email.blade.php @@ -3,10 +3,16 @@ {{ __('Thanks for signing up! Before getting started, could you verify your email address by clicking on the link we just emailed to you? If you didn\'t receive the email, we will gladly send you another.') }} - @if (session('status') == 'verification-link-sent') -
- {{ __('A new verification link has been sent to the email address you provided during registration.') }} -
+ @if(session('status')) + @if (session('status') == 'verification-link-sent') +
+ {{ __('A new verification link has been sent to the email address you provided during registration.') }} +
+ @else +
+ {{ __('در ارسال ایمیل مشکلی پیش امده است. احتمالا به حد اکثر ارسال ایمیل رسیده اید.') }} +
+ @endif @endif
@csrf diff --git a/routes/api.php b/routes/api.php index ccc387f..3821a21 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,8 +1,11 @@ user(); -})->middleware('auth:sanctum'); +//Route::get('/user', function (Request $request) { +// return $request->user(); +//})->middleware('auth:sanctum'); + +Route::get('visit', [VisitController::class, 'store']);