diff --git a/.env.example.json b/.env.example.json index 5c452ccd..24fdd440 100644 --- a/.env.example.json +++ b/.env.example.json @@ -1,6 +1,7 @@ { - "APP_ENV": "development", "APP_NAME": "Bow Application", + "APP_ENV": "development", + "APP_DEBUG": true, "APP_KEY": "", "APP_URL": "http://localhost:5000", @@ -24,17 +25,18 @@ "DB_SOCKET": "", "DB_PREFIX": "", - "SESSION_NAME": "BOW", + "SESSION_DRIVER": "file", + "SESSION_NAME": "BOW_APP", "SESSION_LIFE": 648000, "SESSION_PATH": "/", "SESSION_DOMAIN": null, "SESSION_SECURE": false, "SESSION_HTTPONLY": true, - "S3_KEY": "", - "S3_SECRET": "", - "S3_REGION": "", - "S3_BUCKET": "", + "AWS_KEY": "", + "AWS_SECRET": "", + "AWS_REGION": "us-east-1", + "S3_BUCKET": "buckets", "FTP_HOSTNAME": "localhost", "FTP_PASSWORD": "password", diff --git a/.github/workflows/pull-requests.yml b/.github/workflows/pull-requests.yml index 1bc67d39..33de0af6 100644 --- a/.github/workflows/pull-requests.yml +++ b/.github/workflows/pull-requests.yml @@ -4,9 +4,6 @@ on: pull_request_target: types: [opened] -permissions: - pull-requests: write - jobs: uneditable: uses: bowphp/.github/.github/workflows/pull-requests.yml@main diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 40ab31b8..92a6e0ff 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,16 +1,6 @@ name: Tests -on: - push: - branches: - - master - - '*.x' - pull_request: - schedule: - - cron: '0 0 * * *' - -permissions: - contents: read +on: [ push, pull_request ] jobs: run: diff --git a/.gitignore b/.gitignore index 64e79540..50dfc72a 100755 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,13 @@ -.idea/ -!.gitignore -composer.lock -node_modules -vendor/ -package-lock.json -!.gitkeep -!public/**/.gitkeep -!var/**/.gitkeep -.env.json -config/.key -mix-manifest.json +.idea/ +!.gitignore +composer.lock +node_modules +vendor/ +package-lock.json +!.gitkeep +!public/**/.gitkeep +!var/**/.gitkeep +.env.json +config/.key +mix-manifest.json +.phpunit.result.cache diff --git a/app/Configurations/ApplicationConfiguration.php b/app/Configurations/ApplicationConfiguration.php index 26c037c7..c918d8c2 100644 --- a/app/Configurations/ApplicationConfiguration.php +++ b/app/Configurations/ApplicationConfiguration.php @@ -10,12 +10,12 @@ class ApplicationConfiguration extends Configuration /** * Launch configuration * - * @param Loader $config + * @param Loader $config * @return void */ public function create(Loader $config): void { - // Event::on("user.created", UserCreatedListener::class); + // } /** diff --git a/app/Controllers/Controller.php b/app/Controllers/Controller.php deleted file mode 100644 index 157ba239..00000000 --- a/app/Controllers/Controller.php +++ /dev/null @@ -1,183 +0,0 @@ -all(), $rule); - - return $validation; - } - - /** - * Format API response - * - * @param string $message - * @param string $code - * @param array $data - * @param int $status - * @return array - */ - public function nativeApiErrorResponse( - $message = 'Internal Server Error', - $code = 'INTERNAL_SERVER_ERROR', - $data = [], - $status = 500 - ) { - $time = date('Y-m-d H:i:s'); - $error = compact('message', 'code', 'time'); - - $this->response()->status($status); - - return compact('error', 'data'); - } - - /** - * Fire Event - * - * @param string $event - * @return void - */ - public function emit($event) - { - $data = func_get_args(); - - array_shift($data); - array_unshift($data, $event); - - call_user_func_array('emit_event', $data); - } -} diff --git a/app/Controllers/WelcomeController.php b/app/Controllers/WelcomeController.php index d1df61e0..4769b42d 100644 --- a/app/Controllers/WelcomeController.php +++ b/app/Controllers/WelcomeController.php @@ -3,18 +3,17 @@ namespace App\Controllers; use Bow\Http\Request; -use App\Controllers\Controller; -class WelcomeController extends Controller +class WelcomeController { /** * Show index * - * @param Request $request - * @return string + * @param Request $request + * @return string|null */ public function __invoke(Request $request): ?string { - return $this->render('welcome'); + return view('welcome'); } } diff --git a/app/Exceptions/ErrorHandle.php b/app/Exceptions/ErrorHandle.php index b670ec3a..9885c0fa 100644 --- a/app/Exceptions/ErrorHandle.php +++ b/app/Exceptions/ErrorHandle.php @@ -2,110 +2,46 @@ namespace App\Exceptions; -use Bow\Database\Exception\NotFoundException as ModelNotFoundException; -use Bow\Http\Exception\HttpException; -use Bow\Http\Exception\ResponseException as HttpResponseException; use Exception; -use PDOException; -use Policier\Exception\TokenExpiredException; -use Policier\Exception\TokenInvalidException; +use Bow\Http\Exception\HttpException; +use Bow\Application\Exception\BaseErrorHandler; +use Bow\Database\Exception\NotFoundException as ModelNotFoundException; -class ErrorHandle +class ErrorHandle extends BaseErrorHandler { /** * handle the error * - * @param Exception $exception - * @return void + * @param Exception $exception + * @return mixed|string */ - public function handle(Exception $exception) + public function handle(Exception $exception): mixed { if (request()->isAjax()) { return $this->json($exception); } - if ($exception instanceof ModelNotFoundException || $exception instanceof HttpException) { + if ( + $exception instanceof ModelNotFoundException + || $exception instanceof HttpException + ) { $code = $exception->getStatusCode(); - return $this->render('errors.' . $code, [ - 'code' => 404, - 'exception' => $exception - ]); - } - if ($exception instanceof HttpResponseException) { - return $this->render('errors.500', [ + return $this->render( + 'errors.' . $code, + [ 'code' => 404, 'exception' => $exception - ]); - } - } - - /** - * Render view as response - * - * @param string $view - * @param array $data - * @return mixed - */ - private function render($view, $data = [], $code = 200) - { - if (is_numeric($data)) { - $code = $data; - $data = []; - } - - return view($view, $data, $code); - } - - /** - * Send the json as response - * - * @param string $data - * @param mixed $code - * @return mixed - */ - private function json($exception, $code = null) - { - if ($exception instanceof TokenInvalidException) { - $code = 'TOKEN_INVALID'; - } - - if ($exception instanceof TokenExpiredException) { - $code = 'TOKEN_EXPIRED'; - } - - if (is_null($code)) { - if (method_exists($exception, 'getStatus')) { - $code = $exception->getStatus(); - } else { - $code = 'INTERNAL_SERVER_ERROR'; - } - } - - if (app_env("APP_ENV") == "production" && $exception instanceof PDOException) { - $message = 'Internal error occured'; - } else { - $message = $exception->getMessage(); - } - - $error = [ - 'message' => $message, - 'code' => $code, - 'time' => date('Y-m-d H:i:s') - ]; - - if (config('app.error_trace')) { - $trace = $exception->getTrace(); - } else { - $trace = []; - } - - if ($exception instanceof HttpException) { - $status = $exception->getStatusCode(); - } else { - $status = 500; + ] + ); } - return json(compact('error', 'trace'), $status); + return $this->render( + 'errors.500', + [ + 'code' => 404, + 'exception' => $exception + ] + ); } } diff --git a/app/Kernel.php b/app/Kernel.php index 351af523..7a395cd0 100644 --- a/app/Kernel.php +++ b/app/Kernel.php @@ -2,6 +2,7 @@ namespace App; +use Bow\Router\Router; use Bow\Configuration\Loader as ApplicationLoader; class Kernel extends ApplicationLoader @@ -36,13 +37,14 @@ public function namespaces(): array 'event' => 'App\\Events', 'listener' => 'App\\Listeners', 'exception' => 'App\\Exceptions', - 'producer' => 'App\\Producers', + 'task' => 'App\\Tasks', 'command' => 'App\\Commands', + 'messaging' => 'App\\Messages', ]; } /** - * Define the app middlewares + * Define the app middleware * * @return array */ @@ -67,7 +69,6 @@ public function configurations(): array * Internal configuration of the framework */ \Bow\Configuration\LoggerConfiguration::class, - \Bow\Configuration\EnvConfiguration::class, \Bow\Cache\CacheConfiguration::class, \Bow\Mail\MailConfiguration::class, @@ -101,6 +102,22 @@ public function boot(): ApplicationLoader { parent::boot(); + $this->routes(); + return $this; } + + /** + * Load the define route + * + * @return void + */ + public function routes(): void + { + global $router; + + $router = Router::getInstance(); + + require_once base_path('routes/app.php'); + } } diff --git a/app/Middlewares/AuthenticateMiddleware.php b/app/Middlewares/AuthenticateMiddleware.php index d356be8a..5e9d6931 100644 --- a/app/Middlewares/AuthenticateMiddleware.php +++ b/app/Middlewares/AuthenticateMiddleware.php @@ -11,7 +11,7 @@ class AuthenticateMiddleware extends AuthMiddleware * * @return string */ - public function redirectTo() + public function redirectTo(): string { return '/login'; } diff --git a/app/Middlewares/GuestMiddleware.php b/app/Middlewares/GuestMiddleware.php index e38211e6..956eef69 100644 --- a/app/Middlewares/GuestMiddleware.php +++ b/app/Middlewares/GuestMiddleware.php @@ -11,9 +11,9 @@ class GuestMiddleware implements BaseMiddleware /** * Launch function of the middleware. * - * @param Request $request + * @param Request $request * @param callable $next - * @param array $args + * @param array $args * @return mixed */ public function process(Request $request, callable $next, array $args = []): mixed @@ -30,7 +30,7 @@ public function process(Request $request, callable $next, array $args = []): mix * * @return string */ - public function redirectTo() + public function redirectTo(): string { return '/'; } diff --git a/app/Middlewares/RequestCsrfMiddleware.php b/app/Middlewares/RequestCsrfMiddleware.php index 470b529f..982fc861 100644 --- a/app/Middlewares/RequestCsrfMiddleware.php +++ b/app/Middlewares/RequestCsrfMiddleware.php @@ -9,9 +9,10 @@ class RequestCsrfMiddleware extends CsrfMiddleware /** * {@inheritdoc} */ - public function preventOn() + public function preventOn(): array { return [ + // Add the route pattern for escape the X-CSRF checker ]; } } diff --git a/app/Models/User.php b/app/Models/User.php index bc54caad..3d691f53 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -2,9 +2,15 @@ namespace App\Models; -use Bow\Auth\Authentication as AuthenticatableModel; +use Bow\Auth\Authentication as AuthenticationModel; -class User extends AuthenticatableModel +/** + * @property mixed|string $name + * @property mixed|string $lastname + * @property mixed|string $email + * @property bool|mixed|string $password + */ +class User extends AuthenticationModel { /** * The list of hidden field when toJson is called diff --git a/app/Services/UserService.php b/app/Services/UserService.php index cd66b861..d51e031e 100644 --- a/app/Services/UserService.php +++ b/app/Services/UserService.php @@ -3,6 +3,7 @@ namespace App\Services; use App\Models\User; +use Bow\Database\Collection; class UserService { @@ -21,13 +22,23 @@ public function __construct(User $user) $this->user = $user; } + /** + * Get all available users + * + * @return Collection|null + */ + public function fetchAll(): ?Collection + { + return $this->user->get(); + } + /** * Create new user * - * @param string $name - * @param string $lastname - * @param string $email - * @return User + * @param string $name + * @param string $lastname + * @param string $email + * @return User|null */ public function create(string $name, string $lastname, string $email): ?User { diff --git a/assets/css/app.css b/assets/css/app.css new file mode 100644 index 00000000..b5c61c95 --- /dev/null +++ b/assets/css/app.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/frontend/js/Example.jsx b/assets/js/Example.jsx similarity index 95% rename from frontend/js/Example.jsx rename to assets/js/Example.jsx index 1c109a36..2c39c911 100644 --- a/frontend/js/Example.jsx +++ b/assets/js/Example.jsx @@ -1,16 +1,16 @@ -import React, { Component } from 'react'; -import ReactDOM from 'react-dom'; - -export default class Example extends Component { - render() { - return ( -
- I'm an example React Component. -
- ); - } -} - -if (document.getElementById('main')) { - ReactDOM.render(, document.getElementById('main')); -} +import React, { Component } from 'react'; +import ReactDOM from 'react-dom'; + +export default class Example extends Component { + render() { + return ( +
+ I'm an example React Component. +
+ ); + } +} + +if (document.getElementById('main')) { + ReactDOM.render(, document.getElementById('main')); +} diff --git a/frontend/js/Example.vue b/assets/js/Example.vue similarity index 100% rename from frontend/js/Example.vue rename to assets/js/Example.vue diff --git a/frontend/js/app.js b/assets/js/app.js similarity index 68% rename from frontend/js/app.js rename to assets/js/app.js index a139db8b..36f372c9 100644 --- a/frontend/js/app.js +++ b/assets/js/app.js @@ -1,12 +1,12 @@ -/** - * import Vue from "vue" - * import Example from "./Example.vue" - */ +// import Vue from "vue" +// import Example from "./Example.vue" + window.axios = require('axios'); window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; const token = document.querySelector('meta[name="csrf-token"]'); + if (token) { window.axios.defaults.headers.common['X-Csrf-Token'] = token.getAttribute('content'); } @@ -16,10 +16,8 @@ if (token) { */ require('./Example.jsx'); -/* - * Vue.component('example', Example); - * - * new Vue({ - * el: "#main" - * }); - */ +// Vue.component('example', Example); + +// new Vue({ +// el: "#main" +// }); diff --git a/frontend/sass/_variables.scss b/assets/sass/_variables.scss similarity index 100% rename from frontend/sass/_variables.scss rename to assets/sass/_variables.scss diff --git a/frontend/sass/app.scss b/assets/sass/app.scss similarity index 80% rename from frontend/sass/app.scss rename to assets/sass/app.scss index e7f35b52..b74955d9 100644 --- a/frontend/sass/app.scss +++ b/assets/sass/app.scss @@ -1,9 +1,9 @@ -@import "variables"; - -#main { - position: relative; - margin: 10% auto; - width: 550px; - text-align: center; - font-family: $font-family; -} +@use "variables"; + +#main { + position: relative; + margin: 10% auto; + width: 550px; + text-align: center; + font-family: $font-family; +} diff --git a/bow b/bow index 03da0aa9..f4e43805 100755 --- a/bow +++ b/bow @@ -7,13 +7,13 @@ use Bow\Console\Console; // Register The Auto Loader if (!file_exists(__DIR__."/vendor/autoload.php")) { - die("Please install the depencencies with 'composer update'"); + die("Please install the dependencies with 'composer update'"); } require __DIR__."/vendor/autoload.php"; // Make kernel instance -$kernel = Kernel::configure(__DIR__.'/config'); +$kernel = Kernel::configure(__DIR__)->withConfigPath(__DIR__ . '/config'); // Create command instance and set filename $setting = new Setting(__DIR__); @@ -31,11 +31,12 @@ $setting->setServiceDirectory(__DIR__.'/app/Services'); $setting->setEventDirectory(__DIR__.'/app/Events'); $setting->setEventListenerDirectory(__DIR__.'/app/Listeners'); $setting->setSeederDirectory(__DIR__.'/seeders'); -$setting->setComponentDirectory(__DIR__.'/frontend'); +$setting->setComponentDirectory(__DIR__.'/assets'); $setting->setConfigDirectory(__DIR__.'/config'); $setting->setPublicDirectory(__DIR__.'/public'); -$setting->setProducerDirectory(__DIR__.'/app/Producers'); +$setting->setTaskDirectory(__DIR__.'/app/Tasks'); $setting->setCommandDirectory(__DIR__.'/app/Commands'); +$setting->setNotifierDirectory(__DIR__.'/app/Notifiers'); // Defines the local server starter $setting->setServerFilename(__DIR__.'/server.php'); @@ -49,7 +50,7 @@ $console->bind($kernel); // Load the custom command application if (file_exists(__DIR__.'/routes/console.php')) { - require __DIR__.'/routes/console.php'; + require_once __DIR__.'/routes/console.php'; } // Start console diff --git a/composer.json b/composer.json index 40cc360c..77c17974 100644 --- a/composer.json +++ b/composer.json @@ -16,19 +16,15 @@ }, "require": { "php": "^8.1", - "bowphp/framework": "5.x-dev", - "bowphp/policier": "^2.1", - "ext-openssl": "*", + "bowphp/framework": "^5.2", + "bowphp/policier": "^3.0", "ext-pdo": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-json": "*", - "ext-pdo_mysql": "*", - "ext-intl": "*", - "ext-pdo_sqlite": "*" + "ext-redis": "*", + "ext-curl": "*", + "ext-ftp": "*" }, "require-dev": { - "phpunit/phpunit": "^8", + "phpunit/phpunit": "^9", "monolog/monolog": "^1.22", "squizlabs/php_codesniffer": "3.*" }, @@ -42,7 +38,7 @@ }, "scripts": { "lint": "phpcbf --standard=psr12 --tab-width=4 app tests config migrations seeders routes", - "test": "phpunit --configuration phpunit.dist.xml", + "test": "phpunit --configuration phpunit.xml", "post-root-package-install": [ "@php -r \"file_exists('.env.json') || copy('.env.example.json', '.env.json');\"" ], diff --git a/config/app.php b/config/app.php index 8c5b8bec..b9664045 100644 --- a/config/app.php +++ b/config/app.php @@ -7,10 +7,10 @@ 'name' => app_env('APP_NAME', 'Bow Application'), /** - * The auto csrf enable csrf protected automaticly + * The auto csrf enable csrf protected automatically * on POST, DELETE, PUT */ - "auto_csrf" => app_env("APP_AUTO_CSRF", true), + "auto_csrf" => (bool) app_env("APP_AUTO_CSRF", true), /** * Root of the application @@ -30,9 +30,9 @@ 'env_file' => realpath(__DIR__ . '/../.env.json'), /** - * Path to the frontend folder + * Path to the assets folder */ - 'frontend_path' => dirname(__DIR__) . '/frontend', + 'frontend_path' => dirname(__DIR__) . '/assets', /** * Path to the seeders folder diff --git a/config/cache.php b/config/cache.php index fec74924..9dddd72c 100644 --- a/config/cache.php +++ b/config/cache.php @@ -1,30 +1,30 @@ "file", "stores" => [ + // The filesystem connection "file" => [ "driver" => "file", - "path" => __DIR__ . '/../var/cache' + "path" => __DIR__ . '/../var/cache', + "prefix" => "", ], + // The database connection "database" => [ "driver" => "database", "connection" => app_env('DB_DEFAULT', 'mysql'), "table" => "caches", + "prefix" => "", ], + // The redis connection "redis" => [ 'driver' => 'redis', - 'host' => app_env('REDIS_HOSTNAME', '127.0.0.1'), - 'port' => app_env('REDIS_PORT', 6379), - 'timeout' => 2.5, - 'ssl' => false, - 'username' => app_env('REDIS_USERNAME'), - 'password' => app_env('REDIS_PASSWORD'), - 'database' => app_env('REDIS_CACHE_DB', '1'), - "prefix" => "__app__", + 'database' => app_env('REDIS_CACHE_DB', 5), + "prefix" => "", ] ] ]; diff --git a/config/database.php b/config/database.php index c9736f89..5cc45eac 100644 --- a/config/database.php +++ b/config/database.php @@ -14,16 +14,16 @@ /** * The migration memory table */ - 'migration' => 'bow_migration_status', + 'migration' => 'migrations', /** * The database on which the default application will connect. * - * The database by default, it is on this data base that will connects + * The database by default, it is on this database that will connect * automatically. So you absolutely must not edit the default key. * * In the opposite box you must execute the code in each route. - * `db('the key name')` or `Bow\Database\Database::connection('the key name')` + * `app_db('the key name')` or `Bow\Database\Database::connection('the key name')` */ 'connections' => [ /** @@ -66,6 +66,20 @@ 'charset' => app_env('DB_CHARSET', 'utf8'), 'prefix' => app_env('DB_PREFIX', ''), 'foreign_key_constraints' => app_env('DB_FOREIGN_KEYS', true), - ] + ], + ], + + /** + * Connexion redis + */ + "redis" => [ + 'driver' => 'redis', + 'host' => app_env('REDIS_HOSTNAME', '127.0.0.1'), + 'port' => app_env('REDIS_PORT', 6379), + 'timeout' => 2.5, + 'ssl' => false, + 'username' => app_env('REDIS_USERNAME'), + 'password' => app_env('REDIS_PASSWORD'), + 'database' => app_env('REDIS_CACHE_DB', '1'), ] ]; diff --git a/config/helpers.php b/config/helpers.php index 6790b529..6f0358b6 100644 --- a/config/helpers.php +++ b/config/helpers.php @@ -2,12 +2,13 @@ if (!function_exists('mix')) { /** - * Get mixfile chunkhash version + * Get mix file chunk hash version * - * @param string $path + * @param string $path * @return string + * @throws Exception */ - function mix($path) + function mix(string $path) { $manifest = config('app.mixfile_path'); @@ -31,10 +32,10 @@ function mix($path) /** * Get public directory * - * @param string $path + * @param string $path * @return string */ - function public_path($path = '') + function public_path(string $path = ''): string { return __DIR__ . '/../public/' . ltrim($path, '/'); } @@ -44,10 +45,10 @@ function public_path($path = '') /** * Get frontend directory * - * @param string $path + * @param string $path * @return string */ - function frontend_path($path = '') + function frontend_path(string $path = '') { return __DIR__ . '/../frontend/' . ltrim($path, '/'); } @@ -57,10 +58,10 @@ function frontend_path($path = '') /** * Get storages directory * - * @param string $path + * @param string $path * @return string */ - function storage_path($path = '') + function storage_path(string $path = '') { return __DIR__ . '/../var/' . ltrim($path, '/'); } @@ -72,7 +73,7 @@ function storage_path($path = '') * * @return string */ - function base_path($path = '') + function base_path($path = ''): string { return rtrim(rtrim(realpath(__DIR__ . '/..'), '/') . '/' . $path, '/'); } @@ -83,10 +84,10 @@ function base_path($path = '') * Generate a random code. * Can be used to hide the name of form fields. * - * @param int $len + * @param int $len * @return string */ - function gen_slix($len = 4) + function gen_slix(int $len = 4): string { return substr(str_shuffle(uniqid()), 0, $len); } @@ -98,7 +99,7 @@ function gen_slix($len = 4) * * @return string */ - function gen_unique_id() + function gen_unique_id(): string { $id = base_convert(microtime(false), 10, 36); diff --git a/config/mail.php b/config/mail.php index a98b81cc..703663c5 100644 --- a/config/mail.php +++ b/config/mail.php @@ -20,7 +20,7 @@ "from" => "sender@example.com", /** - * SMTP authentification + * SMTP authentication */ "smtp" => [ "hostname" => app_env("SMTP_HOSTNAME"), @@ -29,11 +29,41 @@ "port" => app_env("SMTP_PORT"), "tls" => app_env("SMTP_TLS"), "ssl" => app_env("SMTP_SSL"), - "timeout" => app_env("SMTP_TIMEOUT") + "timeout" => app_env("SMTP_TIMEOUT"), + + /** + * DKIM (DomainKeys Identified Mail) allows an organization to take + * responsibility for a message by signing it. It allows the receiver + * to verify that the message was not modified in transit. + */ + 'dkim' => [ + 'enabled' => app_env('MAIL_DKIM_ENABLED', false), + 'domain' => app_env('MAIL_DKIM_DOMAIN'), + 'selector' => app_env('MAIL_DKIM_SELECTOR', 'default'), + 'private_key' => app_env('MAIL_DKIM_PRIVATE_KEY'), + 'passphrase' => app_env('MAIL_DKIM_PASSPHRASE'), + 'identity' => app_env('MAIL_DKIM_IDENTITY'), + 'algo' => 'rsa-sha256', + ], + + /** + * SPF (Sender Policy Framework) is an email authentication method designed + * to prevent email spoofing by allowing domain owners to specify which + * mail servers are authorized to send mail for their domains. + */ + 'spf' => [ + 'enabled' => app_env('MAIL_SPF_ENABLED', false), + 'strict' => app_env('MAIL_SPF_STRICT', true), + 'policies' => [ + 'fail' => 'reject', // reject, mark, accept + 'softfail' => 'mark', + 'neutral' => 'accept', + ], + ], ], /** - * SMTP authentification + * SMTP authentication */ "ses" => [ "profile" => app_env("SES_PROFILE", "default"), @@ -51,7 +81,7 @@ */ "mail" => [ "default" => "contact", - "froms" => [ + "from" => [ "contact" => [ "address" => app_env("MAIL_FROM_EMAIL"), "name" => app_env("MAIL_FROM_NAME") @@ -61,5 +91,12 @@ "username" => "Address information" ] ] - ] + ], + + /** + * Log driver configuration + */ + "log" => [ + "path" => sys_get_temp_dir() . '/bow/mails', + ], ]; diff --git a/config/notifier.php b/config/notifier.php new file mode 100644 index 00000000..d58871f9 --- /dev/null +++ b/config/notifier.php @@ -0,0 +1,31 @@ + [ + 'token' => app_env('TELEGRAM_TOKEN'), + 'chat_id' => app_env('TELEGRAM_CHAT_ID'), + ], + + 'slack' => [ + 'token' => app_env('SLACK_TOKEN'), + 'channel' => app_env('SLACK_CHANNEL'), + 'webhook_url' => app_env('SLACK_WEBHOOK_URL'), + ], + + 'sms' => [ + 'provider' => app_env('SMS_PROVIDER', 'callisto'), // Default SMS provider + + 'twilio' => [ + 'account_sid' => app_env('TWILIO_ACCOUNT_SID'), + 'auth_token' => app_env('TWILIO_AUTH_TOKEN'), + 'from' => app_env('TWILIO_FROM'), + ], + + 'callisto' => [ + 'access_key' => app_env('CALLISTO_ACCESS_KEY'), + 'access_secret' => app_env('CALLISTO_ACCESS_SECRET'), + 'notify_url' => app_env('CALLISTO_NOTIFY_URL'), + 'sender' => app_env('CALLISTO_SENDER'), + ], + ], +]; diff --git a/config/policier.php b/config/policier.php index 05a83df1..3de15a3e 100644 --- a/config/policier.php +++ b/config/policier.php @@ -1,7 +1,7 @@ app_env("APP_JWT_SECRET", "FivwuTmpJlwfXB/WMjAyMS0wMS0yNCAyMDozMTozMTE2MTE1MjAyOTEuMDEwOA=="), + "signkey" => app_env("APP_JWT_SECRET"), /** * Token expiration time @@ -13,6 +13,13 @@ */ "iss" => app_env("APP_JWT_ISSUER", "app.example.com"), + /** + * Hashing algorithm being used + * + * HS256, HS384, HS512, ES256, ES384, ES512 + */ + "alg" => "HS512", + /** * Configures the audience */ diff --git a/config/queue.php b/config/queue.php new file mode 100644 index 00000000..15e57da5 --- /dev/null +++ b/config/queue.php @@ -0,0 +1,52 @@ + "sync", + + /** + * The queue drive connection + */ + "connections" => [ + /** + * The sync connexion + */ + "sync" => [ + "queue" => "default", + ], + + /** + * The beanstalkd connexion + */ + "beanstalkd" => [ + "hostname" => "127.0.0.1", + "port" => 11300, + "timeout" => 10, + "queue" => "default", + ], + + /** + * The sqs connexion + */ + "sqs" => [ + "queue" => "default", + "url" => app_env("SQS_URL"), + 'region' => app_env('AWS_REGION'), + 'version' => 'latest', + 'credentials' => [ + 'key' => app_env('AWS_KEY'), + 'secret' => app_env('AWS_SECRET'), + ], + ], + + /** + * The database connexion + */ + "database" => [ + "queue" => "default", + "table" => "queues", + ] + ] +]; diff --git a/config/security.php b/config/security.php index 3a54263a..b0f6a2f3 100644 --- a/config/security.php +++ b/config/security.php @@ -6,7 +6,7 @@ * Can be reorder by the command * `php bow generate:key` */ - 'key' => app_env('APP_KEY', file_get_contents(__DIR__. "/.key")), + 'key' => app_env('APP_KEY'), /** * The Encrypt method @@ -30,7 +30,7 @@ ], /** - * When using token. This is the life time of a token. + * When using token. This is the lifetime of a token. * It is strongly advised to program with tokens. */ 'token_expirate_time' => 50000 diff --git a/config/session.php b/config/session.php index 1053f7a0..b61904e3 100644 --- a/config/session.php +++ b/config/session.php @@ -9,7 +9,7 @@ /** * The session driver */ - 'driver' => 'file', + 'driver' => app_env('SESSION_DRIVER', "file"), /** * The session database drive option @@ -31,7 +31,7 @@ * * @see: http://php.net/manual/fr/session.configuration.php#ini.session.cookie-path. */ - 'path' => '/', + 'path' => app_env('SESSION_PATH', '/'), /** * The cookie domain, for example 'www.example.com'. @@ -40,21 +40,21 @@ * * @see http://php.net/manual/fr/session.configuration.php#ini.session.cookie-domain */ - 'domain' => null, + 'domain' => app_env('SESSION_DOMAIN', 'localhost'), /** * If true, the cookie will only be sent over a secure connection. * * @see: http://php.net/manual/fr/session.configuration.php#ini.session.cookie-secure */ - 'secure' => false, + 'secure' => (bool) app_env('SESSION_SECURE', false), /** * If true, PHP will attempt to send the httponly option when configuring the cookie. * * @see http://php.net/manual/fr/session.configuration.php#ini.session.cookie-httponly */ - 'httponly' => false, + 'httponly' => (bool) app_env('SESSION_HTTPONLY', true), /** * Session data path. @@ -63,6 +63,6 @@ * On some operating systems, you will have to choose a path to a folder * able to handle a large number of small files efficiently. * For example, on Linux, reiserfs can be more efficient than ext2fs. - */ + */ 'save_path' => __DIR__ . '/../var/session', ]; diff --git a/config/storage.php b/config/storage.php index 42bb134c..b3ed31c4 100644 --- a/config/storage.php +++ b/config/storage.php @@ -30,26 +30,27 @@ 'password' => app_env('FTP_PASSWORD'), 'username' => app_env('FTP_USERNAME'), 'port' => app_env('FTP_PORT', 21), - // The basic folder of the server 'root' => app_env('FTP_STARTROOT', null), - // A `true` to activate a secure connection. 'tls' => app_env('FTP_TLS', false), - // Connection waiting time 'timeout' => app_env('FTP_TIMEOUT', 50) ], /** * S3 configuration + * Supports both AWS S3 and MinIO (S3-compatible storage) */ 's3' => [ 'driver' => 's3', + 'bucket' => app_env('S3_BUCKET', 'settlements'), + 'region' => app_env('AWS_REGION', 'us-east-1'), + 'version' => 'latest', 'credentials' => [ - 'key' => app_env('S3_KEY'), - 'secret' => app_env('S3_SECRET'), + 'key' => app_env('AWS_KEY'), + 'secret' => app_env('AWS_SECRET'), ], - 'bucket' => app_env('S3_BUCKET'), - 'region' => app_env('S3_REGION'), - 'version' => 'latest' - ] + // MinIO configuration (optional) + 'endpoint' => app_env('AWS_ENDPOINT'), // e.g., 'http://localhost:9000' for MinIO + 'use_path_style_endpoint' => app_env('AWS_USE_PATH_STYLE_ENDPOINT', false), // Set to true for MinIO + ], ], ]; diff --git a/config/translate.php b/config/translate.php index b7db4db4..20dd61f1 100644 --- a/config/translate.php +++ b/config/translate.php @@ -15,5 +15,5 @@ /** * Path to the language repeater */ - 'dictionary' => __DIR__ . '/../frontend/lang', + 'dictionary' => __DIR__ . '/../lang', ]; diff --git a/config/view.php b/config/view.php index 252bc558..22c5f72a 100644 --- a/config/view.php +++ b/config/view.php @@ -3,7 +3,7 @@ return [ /** * The views directory. It is in this repertory that you will put all your views. - * The views must have the instantion you have defined in 'template_extension' + * The views must have the installation you have defined in 'template_extension' * if no error will be launched */ 'path' => __DIR__ . '/../templates', @@ -17,7 +17,7 @@ * Template supported twig, php, tintin * Define the template name. * Example: define twig with package twig/twig for define twig template - * Bow Framework support actualy twig, tintin, php + * Bow Framework support actually twig, tintin, php */ 'engine' => 'tintin', @@ -36,7 +36,7 @@ /** * Additional option */ - 'aditionnal_options' => [ + 'additional_options' => [ // 'auto_reload_cache' => true ] ]; diff --git a/config/worker.php b/config/worker.php deleted file mode 100644 index 413b2a45..00000000 --- a/config/worker.php +++ /dev/null @@ -1,38 +0,0 @@ - "beanstalkd", - - /** - * The queue drive connection - */ - "connections" => [ - /** - * The sync connexion - */ - "sync" => [ - "directory" => storage_path("cache/queue") - ], - - /** - * The beanstalkd connexion - */ - "beanstalkd" => [ - "hostname" => "127.0.0.0", - "port" => 11300, - "timeout" => 10, - ], - - /** - * The sqs connexion - */ - "sqs" => [ - "hostname" => "127.0.0.0", - "port" => 11300, - "timeout" => 10, - ] - ] -]; diff --git a/frontend/lang/en/validation.php b/frontend/lang/en/validation.php deleted file mode 100644 index ae786e4c..00000000 --- a/frontend/lang/en/validation.php +++ /dev/null @@ -1,25 +0,0 @@ - 'The field {attribute} must be an email.', - 'required' => 'The field {attribute} is required.', - 'empty' => 'The field {attribute} is missing in the fields to be validated.', - 'min' => 'The field {attribute} must be at least {length} characters long.', - 'max' => 'The field {attribute} must not exceed {length} characters.', - 'same' => 'The field {attribute} must be the same as {value}.', - 'number' => 'The field {attribute} must be a number.', - 'int' => 'The field {attribute} must be an integer.', - 'float' => 'The field {attribute} must be a decimal.', - 'alphanum' => 'Only alphanumeric characters are allowed for field {attribute}.', - 'in' => 'The field {attribute} must be one of the following {value}.', - 'size' => 'The field {attribute} must be {length} characters long.', - 'lower' => 'Only lowercase letters are allowed for field {attribute}.', - 'upper' => 'Only uppercase letters are allowed for field {attribute}.', - 'alpha' => 'Only alphabetic characters are allowed for field {attribute}.', - 'exists' => 'The field {attribute} does not exists.', - 'not_exists' => 'The field {attribute} already exists.', - 'unique' => 'The field {attribute} must be unique.', - 'date' => 'The field {attribute} must use the format: yyyy-mm-dd', - 'datetime' => 'The field {attribute} must use the format: yyyy-mm-dd hh:mm:ss', - 'regex' => 'The field {attribute} does not match the pattern', -]; diff --git a/lang/en/validation.php b/lang/en/validation.php new file mode 100644 index 00000000..b0fc4fb2 --- /dev/null +++ b/lang/en/validation.php @@ -0,0 +1,25 @@ + 'The {attribute} field must be an email.', + 'required' => 'The {attribute} field is required.', + 'empty' => 'The {attribute} field is missing in the fields to be validated.', + 'min' => 'The {attribute} field must be at least {length} characters long.', + 'max' => 'The {attribute} field must not exceed {length} characters.', + 'same' => 'The {attribute} field must be the same as {value}.', + 'number' => 'The {attribute} field must be a number.', + 'int' => 'The {attribute} field must be an integer.', + 'float' => 'The {attribute} field must be a decimal.', + 'alphanum' => 'Only alphanumeric characters are allowed for field {attribute}.', + 'in' => 'The {attribute} field must be one of the following {value}.', + 'size' => 'The {attribute} field must be {length} characters long.', + 'lower' => 'Only lowercase letters are allowed for field {attribute}.', + 'upper' => 'Only uppercase letters are allowed for field {attribute}.', + 'alpha' => 'Only alphabetic characters are allowed for field {attribute}.', + 'exists' => 'The {attribute} field does not exists.', + 'not_exists' => 'The {attribute} field already exists.', + 'unique' => 'The {attribute} field must be unique.', + 'date' => 'The {attribute} field must use the format: yyyy-mm-dd', + 'datetime' => 'The {attribute} field must use the format: yyyy-mm-dd hh:mm:ss', + 'regex' => 'The {attribute} field does not match the pattern', +]; diff --git a/frontend/lang/en/welcome.php b/lang/en/welcome.php similarity index 100% rename from frontend/lang/en/welcome.php rename to lang/en/welcome.php diff --git a/frontend/lang/fr/validation.php b/lang/fr/validation.php similarity index 89% rename from frontend/lang/fr/validation.php rename to lang/fr/validation.php index 32cf675a..d53eaf00 100644 --- a/frontend/lang/fr/validation.php +++ b/lang/fr/validation.php @@ -16,9 +16,9 @@ 'lower' => "Le champ {attribute} doit avoir un contenu en miniscule.", 'upper' => "Le champ {attribute} doit avoir un contenu en majiscule.", 'alpha' => "Le champ {attribute} doit avoir un contenu en alphabetique.", - 'exists' => "le champe {attribute} n'existe pas.", - 'not_exists' => "le champ {attribute} existe.", - 'unique' => "le champ {attribute} doit être unique.", + 'exists' => "Le champ {attribute} n'existe pas.", + 'not_exists' => "Le champ {attribute} existe.", + 'unique' => "Le champ {attribute} doit être unique.", 'date' => "Le champ {attribute} n'est pas une date au format yyyy-mm-dd", 'datetime' => "Le champ {attribute} n'est pas une date au format yyyy-mm-dd hh:mm:ss", 'regex' => "Le champ {attribute} n'est pas valide", diff --git a/frontend/lang/fr/welcome.php b/lang/fr/welcome.php similarity index 100% rename from frontend/lang/fr/welcome.php rename to lang/fr/welcome.php diff --git a/migrations/Version20170407084225CreateUsersTable.php b/migrations/Version20170407084225CreateUsersTable.php index 82cff066..7cc3fa6c 100644 --- a/migrations/Version20170407084225CreateUsersTable.php +++ b/migrations/Version20170407084225CreateUsersTable.php @@ -1,7 +1,7 @@ create("users", function (SQLGenerator $table) { + $this->create("users", function (Table $table) { $table->addIncrement('id'); $table->addString('name'); $table->addString('email', ['unique' => true]); diff --git a/package.json b/package.json index adb06129..094e9d8e 100644 --- a/package.json +++ b/package.json @@ -1,32 +1,24 @@ { - "scripts": { - "dev": "npm run development", - "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", - "prod": "npm run production", - "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", - "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", - "hot": "cross-env NODE_ENV=development webpack-dev-server --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", - "format": "composer run-script lint" - }, - "dependencies": { - "axios": ">=0.21.1", - "babel-loader": "^8.0.4", - "bootstrap": "^4.0.0", - "cross-env": "^5.1", - "css-loader": "^2.0.2", - "jquery": "^3.2", - "laravel-mix": "^6.0.49", - "lodash": "^4.17.5", - "popper.js": "^1.12", - "react": "^16.7.0", - "react-dom": "^16.7.0", - "resolve-url-loader": "^5.0.0", - "sass": "^1.15.2", - "sass-loader": "^7.1.0", - "vue": "^2.5.17" - }, - "devDependencies": { - "@babel/preset-react": "^7.0.0", - "vue-template-compiler": "^2.5.21" - } + "private": true, + "type": "module", + "scripts": { + "build": "vite build", + "dev": "vite" + }, + "devDependencies": { + "@tailwindcss/vite": "^4.0.1", + "@vitejs/plugin-react": "^4.3.4", + "@vitejs/plugin-vue": "^5.2.1", + "autoprefixer": "^10.4.20", + "axios": "^1.7.4", + "concurrently": "^9.0.1", + "laravel-vite-plugin": "^1.2.0", + "postcss": "^8.4.47", + "tailwindcss": "^3.4.17", + "vite": "^6.0.11" + }, + "dependencies": { + "react": "^19.0.0", + "react-dom": "^19.0.0" + } } diff --git a/phpunit.xml b/phpunit.xml index 13ee1585..56125fee 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,6 +1,5 @@ - - - - tests/ - - + + + tests/ + + diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 00000000..2aa7205d --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/public/css/app.css b/public/css/app.css index 28698a20..afe577fb 100644 --- a/public/css/app.css +++ b/public/css/app.css @@ -1 +1,584 @@ -#main{position:relative;margin:10% auto;width:550px;text-align:center;font-family:Lato,console,monospace,serif,"sans-serif"} \ No newline at end of file +*, ::before, ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} + +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} + +/* +! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com +*/ + +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +*/ + +*, +::before, +::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: #e5e7eb; + /* 2 */ +} + +::before, +::after { + --tw-content: ''; +} + +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +5. Use the user's configured `sans` font-feature-settings by default. +6. Use the user's configured `sans` font-variation-settings by default. +7. Disable tap highlights on iOS +*/ + +html, +:host { + line-height: 1.5; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ + -moz-tab-size: 4; + /* 3 */ + -o-tab-size: 4; + tab-size: 4; + /* 3 */ + font-family: Figtree, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + /* 4 */ + font-feature-settings: normal; + /* 5 */ + font-variation-settings: normal; + /* 6 */ + -webkit-tap-highlight-color: transparent; + /* 7 */ +} + +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ + +body { + margin: 0; + /* 1 */ + line-height: inherit; + /* 2 */ +} + +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ + +hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ + border-top-width: 1px; + /* 3 */ +} + +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/* +Remove the default font size and weight for headings. +*/ + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ + +a { + color: inherit; + text-decoration: inherit; +} + +/* +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/* +1. Use the user's configured `mono` font-family by default. +2. Use the user's configured `mono` font-feature-settings by default. +3. Use the user's configured `mono` font-variation-settings by default. +4. Correct the odd `em` font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + /* 1 */ + font-feature-settings: normal; + /* 2 */ + font-variation-settings: normal; + /* 3 */ + font-size: 1em; + /* 4 */ +} + +/* +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +3. Remove gaps between table borders by default. +*/ + +table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ + border-collapse: collapse; + /* 3 */ +} + +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-feature-settings: inherit; + /* 1 */ + font-variation-settings: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + font-weight: inherit; + /* 1 */ + line-height: inherit; + /* 1 */ + letter-spacing: inherit; + /* 1 */ + color: inherit; + /* 1 */ + margin: 0; + /* 2 */ + padding: 0; + /* 3 */ +} + +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ + +button, +select { + text-transform: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ + +button, +input:where([type='button']), +input:where([type='reset']), +input:where([type='submit']) { + -webkit-appearance: button; + /* 1 */ + background-color: transparent; + /* 2 */ + background-image: none; + /* 2 */ +} + +/* +Use the modern Firefox focus style for all focusable elements. +*/ + +:-moz-focusring { + outline: auto; +} + +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/* +Removes the default spacing and border for appropriate elements. +*/ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +fieldset { + margin: 0; + padding: 0; +} + +legend { + padding: 0; +} + +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} + +/* +Reset default styling for dialogs. +*/ + +dialog { + padding: 0; +} + +/* +Prevent resizing textareas horizontally by default. +*/ + +textarea { + resize: vertical; +} + +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ + +input::-moz-placeholder, textarea::-moz-placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +/* +Set the default cursor for buttons. +*/ + +button, +[role="button"] { + cursor: pointer; +} + +/* +Make sure disabled buttons don't get the pointer cursor. +*/ + +:disabled { + cursor: default; +} + +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} + +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ + +img, +video { + max-width: 100%; + height: auto; +} + +/* Make elements with the HTML hidden attribute stay hidden by default */ + +[hidden]:where(:not([hidden="until-found"])) { + display: none; +} + +.relative { + position: relative; +} + +.block { + display: block; +} + +.flex { + display: flex; +} + +.hidden { + display: none; +} + +.underline { + text-decoration-line: underline; +} + +.grayscale { + --tw-grayscale: grayscale(100%); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} + +.filter { + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} diff --git a/public/img/logo.png b/public/img/logo.png new file mode 100644 index 00000000..dd384b51 Binary files /dev/null and b/public/img/logo.png differ diff --git a/public/img/logo.svg b/public/img/logo.svg deleted file mode 100644 index 0739265c..00000000 --- a/public/img/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/index.php b/public/index.php index d8e16b97..0d7683ec 100644 --- a/public/index.php +++ b/public/index.php @@ -1,28 +1,27 @@ -bind($kernel); - -// Load application routing -require __DIR__ . "/../routes/app.php"; - -// Run The Application -$app->send(); +bind( + Kernel::configure(dirname(__DIR__))->withConfigPath(dirname(__DIR__) . '/config') +); + +// Run The Application +$app->run(); diff --git a/readme.md b/readme.md index 88e19a7f..c792b83b 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ - + ## Bow Framework @@ -29,8 +29,9 @@ You must make sure the following items are installed on your machine. We would like to extend our thanks to the following sponsors for funding Bow Framework development. If you are interested in becoming a sponsor, please contact [Franck DAKIA](https://github.com/papac): +- [Papac & Co](https://papacandco.com) - [Adjemin](https://adjemin.com) -- [Akil Technologies](https://akiltechnologies.com/) +- [Akil Technologies](https://akiltechnologies.com) - [Etudesk](https://etudesk.com) ## Contributing @@ -40,16 +41,10 @@ Thank you for considering contributing to Bow Framework! The contribution guide - [Franck DAKIA](https://github.com/papac) - [Thank's collaborators](https://github.com/bowphp/app/graphs/contributors) -## Contact - -- [Franck DAKIA](https://github.com/papac) -- [Thank's collaborators](https://github.com/bowphp/docs/graphs/contributors) - 1. Fork it 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request -Please, if there is a bug on the project contact me by email or leave me a message on [Slack](https://bowphp.slack.com). or [join us on Slask](https://join.slack.com/t/bowphp/shared_invite/enQtNzMxOTQ0MTM2ODM5LTQ3MWQ3Mzc1NDFiNDYxMTAyNzBkNDJlMTgwNDJjM2QyMzA2YTk4NDYyN2NiMzM0YTZmNjU1YjBhNmJjZThiM2Q) - +Please, if there is a bug on the project contact me by email or leave me a message on [Telegram](https://t.me/+PiAXH-w9qLUyOTU0) diff --git a/routes/app.php b/routes/app.php index f7357c02..d40657fb 100644 --- a/routes/app.php +++ b/routes/app.php @@ -2,4 +2,4 @@ use App\Controllers\WelcomeController; -$app->get('/', WelcomeController::class)->name('app.index'); +$router->get('/', WelcomeController::class)->name('app.index'); diff --git a/routes/console.php b/routes/console.php index cecf8f90..26dff233 100644 --- a/routes/console.php +++ b/routes/console.php @@ -2,7 +2,11 @@ use Bow\Console\Color; use Bow\Console\Argument; +use App\Commands\TestCommand; $console->addCommand('hello', function (Argument $argument) { + $index = route('app.index'); echo Color::green("hello, bow task runner."); }); + +$console->addCommand('test:hello', TestCommand::class); diff --git a/seeders/20251220174703-user-seeder.php b/seeders/20251220174703-user-seeder.php new file mode 100644 index 00000000..5d403c6f --- /dev/null +++ b/seeders/20251220174703-user-seeder.php @@ -0,0 +1,25 @@ + $faker->name, + 'description' => $faker->text(100), + 'email' => $faker->email, + 'password' => app_hash('password'), + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ]; + + User::create($user); + } + } +} diff --git a/seeders/users.php b/seeders/users.php deleted file mode 100644 index 36957536..00000000 --- a/seeders/users.php +++ /dev/null @@ -1,27 +0,0 @@ - $faker->name, - 'description' => $faker->text, - 'email' => $faker->email, - 'password' => app_hash('password'), - 'created_at' => date('Y-m-d H:i:s'), - 'updated_at' => date('Y-m-d H:i:s'), - ]; -} - -return [User::class => $seeds]; diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 00000000..f6b2f2f9 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,24 @@ +import defaultTheme from "tailwindcss/defaultTheme"; + +/** @type {import('tailwindcss').Config} */ +export default { + content: [ + "./templates/**/*.{blade,tintin}.php", + "./templates/**/*.twig", + "./templates/**/*.{js,jsx,ts,tsx,vue}", + "./assets/js/**/*.{js,jsx,ts,tsx,vue}", + "./var/views/**/*.php", + ], + theme: { + extend: { + fontFamily: { + sans: ["Figtree", ...defaultTheme.fontFamily.sans], + }, + }, + }, + plugins: [ + // require('@tailwindcss/forms'), + // require('@tailwindcss/typography'), + // Add more plugins as needed + ], +}; diff --git a/templates/layouts/default.tintin.php b/templates/layouts/default.tintin.php index 944f7ef8..941c8f62 100644 --- a/templates/layouts/default.tintin.php +++ b/templates/layouts/default.tintin.php @@ -6,8 +6,8 @@ - %inject('title', 'It\'s Worked') - -
- %inject('content') -
+
+ %inject('content') +
diff --git a/templates/welcome.tintin.php b/templates/welcome.tintin.php index cb6b582e..d40bbb88 100644 --- a/templates/welcome.tintin.php +++ b/templates/welcome.tintin.php @@ -1,10 +1,10 @@ %extends('layouts.default') -%block('title', 'It\'s Worked') +%block('title', "It's Worked") %block('content') @@ -12,11 +12,8 @@ adjemin - - akiltechnologie - - - etudesk + + Snapdev Côte d'ivoire %endblock diff --git a/tests/ApplicationTest.php b/tests/ApplicationTest.php index f5acecc4..8205872d 100644 --- a/tests/ApplicationTest.php +++ b/tests/ApplicationTest.php @@ -10,4 +10,11 @@ public function test_show_the_welcome_page() $response->assertStatus(200)->assertContentType('text/html'); } + + public function test_cannot_show_the_not_found_page() + { + $response = $this->get('/not-found-page'); + + $response->assertStatus(404)->assertContentType('text/html'); + } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 35e2c5b9..fdd3c866 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -2,11 +2,11 @@ // Register The Auto Loader if (!file_exists(__DIR__ . "/../vendor/autoload.php")) { - die("Please install the depencencies with 'composer update'"); + die("Please install the dependencies with 'composer update'"); } require __DIR__ . "/../vendor/autoload.php"; // boot kernel -$kernel = App\Kernel::configure(realpath(__DIR__ . '/../config')); +$kernel = App\Kernel::configure(realpath(dirname(__DIR__))); $kernel->boot(); diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 00000000..f11bb773 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,63 @@ + +import { defineConfig } from 'vite'; +import vue from '@vitejs/plugin-vue'; +import react from '@vitejs/plugin-react'; +import path from 'path'; +import tailwindcss from '@tailwindcss/vite'; + +export default defineConfig({ + plugins: [ + vue(), + react(), + tailwindcss(), + ], + root: path.resolve(__dirname, 'assets'), + build: { + outDir: path.resolve(__dirname, 'public'), + emptyOutDir: true, + rollupOptions: { + input: { + app: path.resolve(__dirname, 'assets/js/app.js'), + }, + output: { + entryFileNames: 'js/[name].js', + chunkFileNames: 'js/[name]-[hash].js', + assetFileNames: (assetInfo) => { + // Place images/fonts in their own folders, css in css/ + const ext = assetInfo.name.split('.').pop(); + if (/\.(css|scss|sass|less)$/.test(assetInfo.name)) { + return 'css/[name]-[hash][extname]'; + } + if (/\.(png|jpe?g|gif|svg|webp|ico)$/.test(assetInfo.name)) { + return 'img/[name]-[hash][extname]'; + } + if (/\.(woff2?|eot|ttf|otf)$/.test(assetInfo.name)) { + return 'fonts/[name]-[hash][extname]'; + } + return '[ext]/[name]-[hash][extname]'; + }, + }, + }, + }, + css: { + preprocessorOptions: { + scss: { + additionalData: `@import "${path.resolve(__dirname, 'assets/sass/variables.scss')}";`, + }, + less: { + javascriptEnabled: true, + }, + }, + }, + resolve: { + alias: { + '@': path.resolve(__dirname, 'assets/js'), + '@sass': path.resolve(__dirname, 'assets/sass'), + }, + }, + server: { + watch: { + usePolling: true, + }, + }, +}); diff --git a/webpack.mix.js b/webpack.mix.js deleted file mode 100644 index 25f8d350..00000000 --- a/webpack.mix.js +++ /dev/null @@ -1,4 +0,0 @@ -let mix = require('laravel-mix'); - -mix.react('frontend/js/app.js', 'public/js') - .sass('frontend/sass/app.scss', 'public/css');