diff --git a/Modules/Auth/app/Http/Controllers/.gitkeep b/Modules/Auth/app/Http/Controllers/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/Auth/app/Http/Controllers/AuthController.php b/Modules/Auth/app/Http/Controllers/AuthController.php
new file mode 100644
index 0000000..5714480
--- /dev/null
+++ b/Modules/Auth/app/Http/Controllers/AuthController.php
@@ -0,0 +1,67 @@
+json([
+ 'access_token' => $token,
+ 'token_type' => 'bearer',
+ 'expires_in' => Auth::factory()->getTTL() * 60
+ ]);
+ }
+
+ /**
+ * handle login api
+ *
+ * @param \Illuminate\Http\Request $request
+ * @return void
+ */
+ public function login(Request $request)
+ {
+ $credentials = $request->only('email', 'password');
+
+ if ($token = Auth::attempt($credentials)) {
+ return $this->respondWithToken($token);
+ }
+
+ return response()->json([
+ 'message' => 'Unauthenticated.'
+ ], 401);
+ }
+}
diff --git a/Modules/Auth/app/Http/Controllers/RegisterController.php b/Modules/Auth/app/Http/Controllers/RegisterController.php
new file mode 100644
index 0000000..094c195
--- /dev/null
+++ b/Modules/Auth/app/Http/Controllers/RegisterController.php
@@ -0,0 +1,16 @@
+middleware('auth');
+ }
+
+ public function users()
+ {
+ return User::all();
+ }
+}
diff --git a/Modules/Auth/app/Http/Middleware/.gitkeep b/Modules/Auth/app/Http/Middleware/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/Auth/app/Http/Requests/.gitkeep b/Modules/Auth/app/Http/Requests/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/Auth/app/Models/.gitkeep b/Modules/Auth/app/Models/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/Auth/app/Providers/.gitkeep b/Modules/Auth/app/Providers/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/Auth/app/Providers/AuthServiceProvider.php b/Modules/Auth/app/Providers/AuthServiceProvider.php
new file mode 100644
index 0000000..e2306ad
--- /dev/null
+++ b/Modules/Auth/app/Providers/AuthServiceProvider.php
@@ -0,0 +1,114 @@
+registerCommands();
+ $this->registerCommandSchedules();
+ $this->registerTranslations();
+ $this->registerConfig();
+ $this->registerViews();
+ $this->loadMigrationsFrom(module_path($this->moduleName, 'database/migrations'));
+ }
+
+ /**
+ * Register the service provider.
+ */
+ public function register(): void
+ {
+ $this->app->register(RouteServiceProvider::class);
+ }
+
+ /**
+ * Register commands in the format of Command::class
+ */
+ protected function registerCommands(): void
+ {
+ // $this->commands([]);
+ }
+
+ /**
+ * Register command Schedules.
+ */
+ protected function registerCommandSchedules(): void
+ {
+ // $this->app->booted(function () {
+ // $schedule = $this->app->make(Schedule::class);
+ // $schedule->command('inspire')->hourly();
+ // });
+ }
+
+ /**
+ * Register translations.
+ */
+ public function registerTranslations(): void
+ {
+ $langPath = resource_path('lang/modules/'.$this->moduleNameLower);
+
+ if (is_dir($langPath)) {
+ $this->loadTranslationsFrom($langPath, $this->moduleNameLower);
+ $this->loadJsonTranslationsFrom($langPath);
+ } else {
+ $this->loadTranslationsFrom(module_path($this->moduleName, 'lang'), $this->moduleNameLower);
+ $this->loadJsonTranslationsFrom(module_path($this->moduleName, 'lang'));
+ }
+ }
+
+ /**
+ * Register config.
+ */
+ protected function registerConfig(): void
+ {
+ $this->publishes([module_path($this->moduleName, 'config/config.php') => config_path($this->moduleNameLower.'.php')], 'config');
+ $this->mergeConfigFrom(module_path($this->moduleName, 'config/config.php'), $this->moduleNameLower);
+ }
+
+ /**
+ * Register views.
+ */
+ public function registerViews(): void
+ {
+ $viewPath = resource_path('views/modules/'.$this->moduleNameLower);
+ $sourcePath = module_path($this->moduleName, 'resources/views');
+
+ $this->publishes([$sourcePath => $viewPath], ['views', $this->moduleNameLower.'-module-views']);
+
+ $this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower);
+
+ $componentNamespace = str_replace('/', '\\', config('modules.namespace').'\\'.$this->moduleName.'\\'.config('modules.paths.generator.component-class.path'));
+ Blade::componentNamespace($componentNamespace, $this->moduleNameLower);
+ }
+
+ /**
+ * Get the services provided by the provider.
+ */
+ public function provides(): array
+ {
+ return [];
+ }
+
+ private function getPublishableViewPaths(): array
+ {
+ $paths = [];
+ foreach (config('view.paths') as $path) {
+ if (is_dir($path.'/modules/'.$this->moduleNameLower)) {
+ $paths[] = $path.'/modules/'.$this->moduleNameLower;
+ }
+ }
+
+ return $paths;
+ }
+}
diff --git a/Modules/Auth/app/Providers/RouteServiceProvider.php b/Modules/Auth/app/Providers/RouteServiceProvider.php
new file mode 100644
index 0000000..7d844ec
--- /dev/null
+++ b/Modules/Auth/app/Providers/RouteServiceProvider.php
@@ -0,0 +1,59 @@
+mapApiRoutes();
+
+ $this->mapWebRoutes();
+ }
+
+ /**
+ * Define the "web" routes for the application.
+ *
+ * These routes all receive session state, CSRF protection, etc.
+ */
+ protected function mapWebRoutes(): void
+ {
+ Route::middleware('web')
+ ->namespace($this->moduleNamespace)
+ ->group(module_path('Auth', '/routes/web.php'));
+ }
+
+ /**
+ * Define the "api" routes for the application.
+ *
+ * These routes are typically stateless.
+ */
+ protected function mapApiRoutes(): void
+ {
+ Route::prefix('api')
+ ->middleware('api')
+ ->namespace($this->moduleNamespace)
+ ->group(module_path('Auth', '/routes/api.php'));
+ }
+}
diff --git a/Modules/Auth/composer.json b/Modules/Auth/composer.json
new file mode 100644
index 0000000..cc2d0ff
--- /dev/null
+++ b/Modules/Auth/composer.json
@@ -0,0 +1,31 @@
+{
+ "name": "nwidart/auth",
+ "description": "",
+ "authors": [
+ {
+ "name": "Nicolas Widart",
+ "email": "n.widart@gmail.com"
+ }
+ ],
+ "extra": {
+ "laravel": {
+ "providers": [],
+ "aliases": {
+
+ }
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Modules\\Auth\\": "",
+ "Modules\\Auth\\App\\": "app/",
+ "Modules\\Auth\\Database\\Factories\\": "database/factories/",
+ "Modules\\Auth\\Database\\Seeders\\": "database/seeders/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Modules\\Auth\\Tests\\": "tests/"
+ }
+ }
+}
diff --git a/Modules/Auth/config/.gitkeep b/Modules/Auth/config/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/Auth/config/config.php b/Modules/Auth/config/config.php
new file mode 100644
index 0000000..cba1be8
--- /dev/null
+++ b/Modules/Auth/config/config.php
@@ -0,0 +1,5 @@
+ 'Auth',
+];
diff --git a/Modules/Auth/database/factories/.gitkeep b/Modules/Auth/database/factories/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/Auth/database/migrations/.gitkeep b/Modules/Auth/database/migrations/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/Auth/database/seeders/.gitkeep b/Modules/Auth/database/seeders/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/Auth/database/seeders/AuthDatabaseSeeder.php b/Modules/Auth/database/seeders/AuthDatabaseSeeder.php
new file mode 100644
index 0000000..1c82191
--- /dev/null
+++ b/Modules/Auth/database/seeders/AuthDatabaseSeeder.php
@@ -0,0 +1,16 @@
+call([]);
+ }
+}
diff --git a/Modules/Auth/lang/.gitkeep b/Modules/Auth/lang/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/Auth/module.json b/Modules/Auth/module.json
new file mode 100644
index 0000000..ec313d7
--- /dev/null
+++ b/Modules/Auth/module.json
@@ -0,0 +1,11 @@
+{
+ "name": "Auth",
+ "alias": "auth",
+ "description": "",
+ "keywords": [],
+ "priority": 0,
+ "providers": [
+ "Modules\\Auth\\app\\Providers\\AuthServiceProvider"
+ ],
+ "files": []
+}
diff --git a/Modules/Auth/package.json b/Modules/Auth/package.json
new file mode 100644
index 0000000..d6fbfc8
--- /dev/null
+++ b/Modules/Auth/package.json
@@ -0,0 +1,15 @@
+{
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build"
+ },
+ "devDependencies": {
+ "axios": "^1.1.2",
+ "laravel-vite-plugin": "^0.7.5",
+ "sass": "^1.69.5",
+ "postcss": "^8.3.7",
+ "vite": "^4.0.0"
+ }
+}
diff --git a/Modules/Auth/resources/assets/.gitkeep b/Modules/Auth/resources/assets/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/Auth/resources/assets/js/app.js b/Modules/Auth/resources/assets/js/app.js
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/Auth/resources/assets/sass/app.scss b/Modules/Auth/resources/assets/sass/app.scss
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/Auth/resources/views/.gitkeep b/Modules/Auth/resources/views/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/Auth/resources/views/index.blade.php b/Modules/Auth/resources/views/index.blade.php
new file mode 100644
index 0000000..9584bd0
--- /dev/null
+++ b/Modules/Auth/resources/views/index.blade.php
@@ -0,0 +1,7 @@
+@extends('auth::layouts.master')
+
+@section('content')
+
Hello World
+
+ Module: {!! config('auth.name') !!}
+@endsection
diff --git a/Modules/Auth/resources/views/layouts/master.blade.php b/Modules/Auth/resources/views/layouts/master.blade.php
new file mode 100644
index 0000000..0398d21
--- /dev/null
+++ b/Modules/Auth/resources/views/layouts/master.blade.php
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+ Auth Module - {{ config('app.name', 'Laravel') }}
+
+
+
+
+
+
+
+
+
+ {{-- Vite CSS --}}
+ {{-- {{ module_vite('build-auth', 'resources/assets/sass/app.scss') }} --}}
+
+
+
+ @yield('content')
+
+ {{-- Vite JS --}}
+ {{-- {{ module_vite('build-auth', 'resources/assets/js/app.js') }} --}}
+
diff --git a/Modules/Auth/routes/.gitkeep b/Modules/Auth/routes/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/Auth/routes/api.php b/Modules/Auth/routes/api.php
new file mode 100644
index 0000000..9cd9c9b
--- /dev/null
+++ b/Modules/Auth/routes/api.php
@@ -0,0 +1,29 @@
+prefix('v1')
+ ->name('api.v1.')
+ ->group(function () {
+ Route::get('users', [UserController::class, 'users']);
+ Route::post('login', [LoginController::class, 'login']);
+ Route::post('register', [RegisterController::class, 'register']);
+ Route::post('forgot', [ForgotController::class, 'forgot']);
+ });
diff --git a/Modules/Auth/routes/web.php b/Modules/Auth/routes/web.php
new file mode 100644
index 0000000..9c69967
--- /dev/null
+++ b/Modules/Auth/routes/web.php
@@ -0,0 +1,19 @@
+names('auth');
+});
diff --git a/Modules/Auth/tests/Feature/.gitkeep b/Modules/Auth/tests/Feature/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/Auth/tests/Unit/.gitkeep b/Modules/Auth/tests/Unit/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Modules/Auth/vite.config.js b/Modules/Auth/vite.config.js
new file mode 100644
index 0000000..42933d0
--- /dev/null
+++ b/Modules/Auth/vite.config.js
@@ -0,0 +1,26 @@
+import { defineConfig } from 'vite';
+import laravel from 'laravel-vite-plugin';
+
+export default defineConfig({
+ build: {
+ outDir: '../../public/build-auth',
+ emptyOutDir: true,
+ manifest: true,
+ },
+ plugins: [
+ laravel({
+ publicDirectory: '../../public',
+ buildDirectory: 'build-auth',
+ input: [
+ __dirname + '/resources/assets/sass/app.scss',
+ __dirname + '/resources/assets/js/app.js'
+ ],
+ refresh: true,
+ }),
+ ],
+});
+
+//export const paths = [
+// 'Modules/$STUDLY_NAME$/resources/assets/sass/app.scss',
+// 'Modules/$STUDLY_NAME$/resources/assets/js/app.js',
+//];
\ No newline at end of file
diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php
index 56af264..c82b485 100644
--- a/app/Exceptions/Handler.php
+++ b/app/Exceptions/Handler.php
@@ -2,7 +2,9 @@
namespace App\Exceptions;
+use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
+use Illuminate\Http\JsonResponse;
use Throwable;
class Handler extends ExceptionHandler
@@ -27,4 +29,33 @@ class Handler extends ExceptionHandler
//
});
}
+
+ /**
+ * Render an exception into an HTTP response.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param \Throwable $e
+ * @return \Symfony\Component\HttpFoundation\Response
+ *
+ * @throws \Throwable
+ */
+ public function render($request, Throwable $exception)
+ {
+ if ($request->expectsJson()) {
+ }
+
+ return $this->prepareJsonResponse($request, $exception);
+ return parent::render($request, $exception);
+ }
+
+ protected function prepareJsonResponse($request, Throwable $exception)
+ {
+ return new JsonResponse(
+ [
+ 'message' => $exception->getMessage(),
+ 'code' => $exception->getCode(),
+ ],
+ $this->isHttpException($exception) ? $exception->getStatusCode() : Response::HTTP_INTERNAL_SERVER_ERROR
+ );
+ }
}
diff --git a/app/Helpers/Helper.php b/app/Helpers/Helper.php
new file mode 100644
index 0000000..b3514d9
--- /dev/null
+++ b/app/Helpers/Helper.php
@@ -0,0 +1,6 @@
+expectsJson() ? null : route('login');
+ throw new HttpResponseException(
+ Response::json([
+ 'message' => 'Unauthenticated.'
+ ], 401)
+ );
+ }
+
+ /**
+ * Handle an unauthenticated user.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @return void
+ *
+ * @throws \Illuminate\Http\Exceptions\HttpResponseException
+ */
+ public function responseUnauthenticated($request)
+ {
+ return $this->unauthenticated($request, []);
}
}
diff --git a/app/Models/User.php b/app/Models/User.php
index 4d7f70f..c585b7e 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -7,8 +7,9 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
+use Tymon\JWTAuth\Contracts\JWTSubject;
-class User extends Authenticatable
+class User extends Authenticatable implements JWTSubject
{
use HasApiTokens, HasFactory, Notifiable;
@@ -42,4 +43,24 @@ class User extends Authenticatable
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
+
+ /**
+ * Get the identifier that will be stored in the subject claim of the JWT.
+ *
+ * @return mixed
+ */
+ public function getJWTIdentifier()
+ {
+ return $this->getKey();
+ }
+
+ /**
+ * Return a key value array, containing any custom claims to be added to the JWT.
+ *
+ * @return array
+ */
+ public function getJWTCustomClaims()
+ {
+ return [];
+ }
}
diff --git a/config/app.php b/config/app.php
index 9207160..968af5e 100644
--- a/config/app.php
+++ b/config/app.php
@@ -168,6 +168,7 @@ return [
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
+ Tymon\JWTAuth\Providers\LaravelServiceProvider::class,
])->toArray(),
/*
diff --git a/config/auth.php b/config/auth.php
index 9548c15..cd245b7 100644
--- a/config/auth.php
+++ b/config/auth.php
@@ -40,6 +40,10 @@ return [
'driver' => 'session',
'provider' => 'users',
],
+ 'api' => [
+ 'driver' => 'jwt',
+ 'provider' => 'users',
+ ],
],
/*
diff --git a/config/jwt.php b/config/jwt.php
new file mode 100644
index 0000000..f83234d
--- /dev/null
+++ b/config/jwt.php
@@ -0,0 +1,301 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return [
+
+ /*
+ |--------------------------------------------------------------------------
+ | JWT Authentication Secret
+ |--------------------------------------------------------------------------
+ |
+ | Don't forget to set this in your .env file, as it will be used to sign
+ | your tokens. A helper command is provided for this:
+ | `php artisan jwt:secret`
+ |
+ | Note: This will be used for Symmetric algorithms only (HMAC),
+ | since RSA and ECDSA use a private/public key combo (See below).
+ |
+ */
+
+ 'secret' => env('JWT_SECRET'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | JWT Authentication Keys
+ |--------------------------------------------------------------------------
+ |
+ | The algorithm you are using, will determine whether your tokens are
+ | signed with a random string (defined in `JWT_SECRET`) or using the
+ | following public & private keys.
+ |
+ | Symmetric Algorithms:
+ | HS256, HS384 & HS512 will use `JWT_SECRET`.
+ |
+ | Asymmetric Algorithms:
+ | RS256, RS384 & RS512 / ES256, ES384 & ES512 will use the keys below.
+ |
+ */
+
+ 'keys' => [
+
+ /*
+ |--------------------------------------------------------------------------
+ | Public Key
+ |--------------------------------------------------------------------------
+ |
+ | A path or resource to your public key.
+ |
+ | E.g. 'file://path/to/public/key'
+ |
+ */
+
+ 'public' => env('JWT_PUBLIC_KEY'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Private Key
+ |--------------------------------------------------------------------------
+ |
+ | A path or resource to your private key.
+ |
+ | E.g. 'file://path/to/private/key'
+ |
+ */
+
+ 'private' => env('JWT_PRIVATE_KEY'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Passphrase
+ |--------------------------------------------------------------------------
+ |
+ | The passphrase for your private key. Can be null if none set.
+ |
+ */
+
+ 'passphrase' => env('JWT_PASSPHRASE'),
+
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | JWT time to live
+ |--------------------------------------------------------------------------
+ |
+ | Specify the length of time (in minutes) that the token will be valid for.
+ | Defaults to 1 hour.
+ |
+ | You can also set this to null, to yield a never expiring token.
+ | Some people may want this behaviour for e.g. a mobile app.
+ | This is not particularly recommended, so make sure you have appropriate
+ | systems in place to revoke the token if necessary.
+ | Notice: If you set this to null you should remove 'exp' element from 'required_claims' list.
+ |
+ */
+
+ 'ttl' => env('JWT_TTL', 60),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Refresh time to live
+ |--------------------------------------------------------------------------
+ |
+ | Specify the length of time (in minutes) that the token can be refreshed
+ | within. I.E. The user can refresh their token within a 2 week window of
+ | the original token being created until they must re-authenticate.
+ | Defaults to 2 weeks.
+ |
+ | You can also set this to null, to yield an infinite refresh time.
+ | Some may want this instead of never expiring tokens for e.g. a mobile app.
+ | This is not particularly recommended, so make sure you have appropriate
+ | systems in place to revoke the token if necessary.
+ |
+ */
+
+ 'refresh_ttl' => env('JWT_REFRESH_TTL', 20160),
+
+ /*
+ |--------------------------------------------------------------------------
+ | JWT hashing algorithm
+ |--------------------------------------------------------------------------
+ |
+ | Specify the hashing algorithm that will be used to sign the token.
+ |
+ */
+
+ 'algo' => env('JWT_ALGO', Tymon\JWTAuth\Providers\JWT\Provider::ALGO_HS256),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Required Claims
+ |--------------------------------------------------------------------------
+ |
+ | Specify the required claims that must exist in any token.
+ | A TokenInvalidException will be thrown if any of these claims are not
+ | present in the payload.
+ |
+ */
+
+ 'required_claims' => [
+ 'iss',
+ 'iat',
+ 'exp',
+ 'nbf',
+ 'sub',
+ 'jti',
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Persistent Claims
+ |--------------------------------------------------------------------------
+ |
+ | Specify the claim keys to be persisted when refreshing a token.
+ | `sub` and `iat` will automatically be persisted, in
+ | addition to the these claims.
+ |
+ | Note: If a claim does not exist then it will be ignored.
+ |
+ */
+
+ 'persistent_claims' => [
+ // 'foo',
+ // 'bar',
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Lock Subject
+ |--------------------------------------------------------------------------
+ |
+ | This will determine whether a `prv` claim is automatically added to
+ | the token. The purpose of this is to ensure that if you have multiple
+ | authentication models e.g. `App\User` & `App\OtherPerson`, then we
+ | should prevent one authentication request from impersonating another,
+ | if 2 tokens happen to have the same id across the 2 different models.
+ |
+ | Under specific circumstances, you may want to disable this behaviour
+ | e.g. if you only have one authentication model, then you would save
+ | a little on token size.
+ |
+ */
+
+ 'lock_subject' => true,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Leeway
+ |--------------------------------------------------------------------------
+ |
+ | This property gives the jwt timestamp claims some "leeway".
+ | Meaning that if you have any unavoidable slight clock skew on
+ | any of your servers then this will afford you some level of cushioning.
+ |
+ | This applies to the claims `iat`, `nbf` and `exp`.
+ |
+ | Specify in seconds - only if you know you need it.
+ |
+ */
+
+ 'leeway' => env('JWT_LEEWAY', 0),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Blacklist Enabled
+ |--------------------------------------------------------------------------
+ |
+ | In order to invalidate tokens, you must have the blacklist enabled.
+ | If you do not want or need this functionality, then set this to false.
+ |
+ */
+
+ 'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true),
+
+ /*
+ | -------------------------------------------------------------------------
+ | Blacklist Grace Period
+ | -------------------------------------------------------------------------
+ |
+ | When multiple concurrent requests are made with the same JWT,
+ | it is possible that some of them fail, due to token regeneration
+ | on every request.
+ |
+ | Set grace period in seconds to prevent parallel request failure.
+ |
+ */
+
+ 'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Cookies encryption
+ |--------------------------------------------------------------------------
+ |
+ | By default Laravel encrypt cookies for security reason.
+ | If you decide to not decrypt cookies, you will have to configure Laravel
+ | to not encrypt your cookie token by adding its name into the $except
+ | array available in the middleware "EncryptCookies" provided by Laravel.
+ | see https://laravel.com/docs/master/responses#cookies-and-encryption
+ | for details.
+ |
+ | Set it to true if you want to decrypt cookies.
+ |
+ */
+
+ 'decrypt_cookies' => false,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Providers
+ |--------------------------------------------------------------------------
+ |
+ | Specify the various providers used throughout the package.
+ |
+ */
+
+ 'providers' => [
+
+ /*
+ |--------------------------------------------------------------------------
+ | JWT Provider
+ |--------------------------------------------------------------------------
+ |
+ | Specify the provider that is used to create and decode the tokens.
+ |
+ */
+
+ 'jwt' => Tymon\JWTAuth\Providers\JWT\Lcobucci::class,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Authentication Provider
+ |--------------------------------------------------------------------------
+ |
+ | Specify the provider that is used to authenticate users.
+ |
+ */
+
+ 'auth' => Tymon\JWTAuth\Providers\Auth\Illuminate::class,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Storage Provider
+ |--------------------------------------------------------------------------
+ |
+ | Specify the provider that is used to store tokens in the blacklist.
+ |
+ */
+
+ 'storage' => Tymon\JWTAuth\Providers\Storage\Illuminate::class,
+
+ ],
+
+];
diff --git a/config/modules.php b/config/modules.php
new file mode 100644
index 0000000..d9b24cb
--- /dev/null
+++ b/config/modules.php
@@ -0,0 +1,285 @@
+ 'Modules',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Module Stubs
+ |--------------------------------------------------------------------------
+ |
+ | Default module stubs.
+ |
+ */
+
+ 'stubs' => [
+ 'enabled' => false,
+ 'path' => base_path('vendor/nwidart/laravel-modules/src/Commands/stubs'),
+ 'files' => [
+ 'routes/web' => 'routes/web.php',
+ 'routes/api' => 'routes/api.php',
+ 'views/index' => 'resources/views/index.blade.php',
+ 'views/master' => 'resources/views/layouts/master.blade.php',
+ 'scaffold/config' => 'config/config.php',
+ 'composer' => 'composer.json',
+ 'assets/js/app' => 'resources/assets/js/app.js',
+ 'assets/sass/app' => 'resources/assets/sass/app.scss',
+ 'vite' => 'vite.config.js',
+ 'package' => 'package.json',
+ ],
+ 'replacements' => [
+ 'routes/web' => ['LOWER_NAME', 'STUDLY_NAME', 'MODULE_NAMESPACE', 'CONTROLLER_NAMESPACE'],
+ 'routes/api' => ['LOWER_NAME', 'STUDLY_NAME'],
+ 'vite' => ['LOWER_NAME'],
+ 'json' => ['LOWER_NAME', 'STUDLY_NAME', 'MODULE_NAMESPACE', 'PROVIDER_NAMESPACE'],
+ 'views/index' => ['LOWER_NAME'],
+ 'views/master' => ['LOWER_NAME', 'STUDLY_NAME'],
+ 'scaffold/config' => ['STUDLY_NAME'],
+ 'composer' => [
+ 'LOWER_NAME',
+ 'STUDLY_NAME',
+ 'VENDOR',
+ 'AUTHOR_NAME',
+ 'AUTHOR_EMAIL',
+ 'MODULE_NAMESPACE',
+ 'PROVIDER_NAMESPACE',
+ ],
+ ],
+ 'gitkeep' => true,
+ ],
+ 'paths' => [
+ /*
+ |--------------------------------------------------------------------------
+ | Modules path
+ |--------------------------------------------------------------------------
+ |
+ | This path is used to save the generated module.
+ | This path will also be added automatically to the list of scanned folders.
+ |
+ */
+
+ 'modules' => base_path('Modules'),
+ /*
+ |--------------------------------------------------------------------------
+ | Modules assets path
+ |--------------------------------------------------------------------------
+ |
+ | Here you may update the modules' assets path.
+ |
+ */
+
+ 'assets' => public_path('modules'),
+ /*
+ |--------------------------------------------------------------------------
+ | The migrations' path
+ |--------------------------------------------------------------------------
+ |
+ | Where you run the 'module:publish-migration' command, where do you publish the
+ | the migration files?
+ |
+ */
+
+ 'migration' => base_path('database/migrations'),
+ /*
+ |--------------------------------------------------------------------------
+ | Generator path
+ |--------------------------------------------------------------------------
+ | Customise the paths where the folders will be generated.
+ | Set the generate's key to false to not generate that folder
+ */
+ 'generator' => [
+ 'config' => ['path' => 'config', 'generate' => true],
+ 'command' => ['path' => 'app/Console', 'generate' => false],
+ 'channels' => ['path' => 'app/Broadcasting', 'generate' => false],
+ 'migration' => ['path' => 'database/migrations', 'generate' => true],
+ 'seeder' => [
+ 'path' => 'database/seeders',
+ 'namespace' => 'database/seeders',
+ 'generate' => true,
+ ],
+ 'factory' => ['path' => 'database/factories', 'generate' => true],
+ 'model' => ['path' => 'app/Models', 'generate' => true],
+ 'observer' => ['path' => 'app/Observers', 'generate' => false],
+ 'routes' => ['path' => 'routes', 'generate' => true],
+ 'controller' => ['path' => 'app/Http/Controllers', 'generate' => true],
+ 'filter' => ['path' => 'app/Http/Middleware', 'generate' => true],
+ 'request' => ['path' => 'app/Http/Requests', 'generate' => true],
+ 'provider' => ['path' => 'app/Providers', 'generate' => true],
+ 'assets' => ['path' => 'resources/assets', 'generate' => true],
+ 'lang' => ['path' => 'lang', 'generate' => true],
+ 'views' => ['path' => 'resources/views', 'generate' => true],
+ 'test' => ['path' => 'tests/Unit', 'generate' => true],
+ 'test-feature' => ['path' => 'tests/Feature', 'generate' => true],
+ 'repository' => ['path' => 'app/Repositories', 'generate' => false],
+ 'event' => ['path' => 'app/Events', 'generate' => false],
+ 'listener' => ['path' => 'app/Listeners', 'generate' => false],
+ 'policies' => ['path' => 'app/Policies', 'generate' => false],
+ 'rules' => ['path' => 'app/Rules', 'generate' => false],
+ 'jobs' => ['path' => 'app/Jobs', 'generate' => false],
+ 'emails' => ['path' => 'app/Emails', 'generate' => false],
+ 'notifications' => ['path' => 'app/Notifications', 'generate' => false],
+ 'resource' => ['path' => 'app/Resources', 'generate' => false],
+ 'component-view' => ['path' => 'resources/views/components', 'generate' => false],
+ 'component-class' => ['path' => 'app/View/Components', 'generate' => false],
+ ],
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Package commands
+ |--------------------------------------------------------------------------
+ |
+ | Here you can define which commands will be visible and used in your
+ | application. If for example, you don't use some of the commands provided
+ | you can simply comment them out.
+ |
+ */
+ 'commands' => [
+ Commands\CommandMakeCommand::class,
+ Commands\ComponentClassMakeCommand::class,
+ Commands\ComponentViewMakeCommand::class,
+ Commands\ControllerMakeCommand::class,
+ Commands\ChannelMakeCommand::class,
+ Commands\DisableCommand::class,
+ Commands\DumpCommand::class,
+ Commands\EnableCommand::class,
+ Commands\EventMakeCommand::class,
+ Commands\FactoryMakeCommand::class,
+ Commands\JobMakeCommand::class,
+ Commands\ListenerMakeCommand::class,
+ Commands\MailMakeCommand::class,
+ Commands\MiddlewareMakeCommand::class,
+ Commands\NotificationMakeCommand::class,
+ Commands\ObserverMakeCommand::class,
+ Commands\PolicyMakeCommand::class,
+ Commands\ProviderMakeCommand::class,
+ Commands\InstallCommand::class,
+ Commands\LaravelModulesV6Migrator::class,
+ Commands\ListCommand::class,
+ Commands\ModuleDeleteCommand::class,
+ Commands\ModuleMakeCommand::class,
+ Commands\MigrateCommand::class,
+ Commands\MigrateFreshCommand::class,
+ Commands\MigrateRefreshCommand::class,
+ Commands\MigrateResetCommand::class,
+ Commands\MigrateRollbackCommand::class,
+ Commands\MigrateStatusCommand::class,
+ Commands\MigrationMakeCommand::class,
+ Commands\ModelMakeCommand::class,
+ Commands\ResourceMakeCommand::class,
+ Commands\RequestMakeCommand::class,
+ Commands\RuleMakeCommand::class,
+ Commands\RouteProviderMakeCommand::class,
+ Commands\PublishCommand::class,
+ Commands\PublishConfigurationCommand::class,
+ Commands\PublishMigrationCommand::class,
+ Commands\PublishTranslationCommand::class,
+ Commands\SeedCommand::class,
+ Commands\SeedMakeCommand::class,
+ Commands\SetupCommand::class,
+ Commands\TestMakeCommand::class,
+ Commands\UnUseCommand::class,
+ Commands\UpdateCommand::class,
+ Commands\UseCommand::class,
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Scan Path
+ |--------------------------------------------------------------------------
+ |
+ | Here you define which folder will be scanned. By default will scan vendor
+ | directory. This is useful if you host the package in packagist website.
+ |
+ */
+
+ 'scan' => [
+ 'enabled' => false,
+ 'paths' => [
+ base_path('vendor/*/*'),
+ ],
+ ],
+ /*
+ |--------------------------------------------------------------------------
+ | Composer File Template
+ |--------------------------------------------------------------------------
+ |
+ | Here is the config for the composer.json file, generated by this package
+ |
+ */
+
+ 'composer' => [
+ 'vendor' => 'nwidart',
+ 'author' => [
+ 'name' => 'Nicolas Widart',
+ 'email' => 'n.widart@gmail.com',
+ ],
+ 'composer-output' => false,
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Caching
+ |--------------------------------------------------------------------------
+ |
+ | Here is the config for setting up the caching feature.
+ |
+ */
+ 'cache' => [
+ 'enabled' => false,
+ 'driver' => 'file',
+ 'key' => 'laravel-modules',
+ 'lifetime' => 60,
+ ],
+ /*
+ |--------------------------------------------------------------------------
+ | Choose what laravel-modules will register as custom namespaces.
+ | Setting one to false will require you to register that part
+ | in your own Service Provider class.
+ |--------------------------------------------------------------------------
+ */
+ 'register' => [
+ 'translations' => true,
+ /**
+ * load files on boot or register method
+ *
+ * Note: boot not compatible with asgardcms
+ *
+ * @example boot|register
+ */
+ 'files' => 'register',
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Activators
+ |--------------------------------------------------------------------------
+ |
+ | You can define new types of activators here, file, database, etc. The only
+ | required parameter is 'class'.
+ | The file activator will store the activation status in storage/installed_modules
+ */
+ 'activators' => [
+ 'file' => [
+ 'class' => FileActivator::class,
+ 'statuses-file' => base_path('modules_statuses.json'),
+ 'cache-key' => 'activator.installed',
+ 'cache-lifetime' => 604800,
+ ],
+ ],
+
+ 'activator' => 'file',
+];
diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php
index a9f4519..0c2e517 100644
--- a/database/seeders/DatabaseSeeder.php
+++ b/database/seeders/DatabaseSeeder.php
@@ -12,11 +12,9 @@ class DatabaseSeeder extends Seeder
*/
public function run(): void
{
- // \App\Models\User::factory(10)->create();
-
- // \App\Models\User::factory()->create([
- // 'name' => 'Test User',
- // 'email' => 'test@example.com',
- // ]);
+ $this->call([
+ UserSeeder::class,
+ // Add other seeders if necessary
+ ]);
}
}
diff --git a/database/seeders/UserSeeder.php b/database/seeders/UserSeeder.php
new file mode 100644
index 0000000..52287e5
--- /dev/null
+++ b/database/seeders/UserSeeder.php
@@ -0,0 +1,26 @@
+find(['email' => 'admin@apactech.io'])) {
+ $user->factory()->createOne([
+ 'name' => 'admin',
+ 'email' => 'admin@apactech.io',
+ 'password' => bcrypt('admin123')
+ ]);
+ }
+ $user->factory(10)->create();
+ dump($user->get()->toArray());
+ }
+}
diff --git a/stubs/nwidart-stubs/assets/js/app.stub b/stubs/nwidart-stubs/assets/js/app.stub
new file mode 100644
index 0000000..e69de29
diff --git a/stubs/nwidart-stubs/assets/sass/app.stub b/stubs/nwidart-stubs/assets/sass/app.stub
new file mode 100644
index 0000000..e69de29
diff --git a/stubs/nwidart-stubs/channel.stub b/stubs/nwidart-stubs/channel.stub
new file mode 100644
index 0000000..f68b0b0
--- /dev/null
+++ b/stubs/nwidart-stubs/channel.stub
@@ -0,0 +1,24 @@
+
+
+
diff --git a/stubs/nwidart-stubs/composer.stub b/stubs/nwidart-stubs/composer.stub
new file mode 100644
index 0000000..7d75c42
--- /dev/null
+++ b/stubs/nwidart-stubs/composer.stub
@@ -0,0 +1,31 @@
+{
+ "name": "$VENDOR$/$LOWER_NAME$",
+ "description": "",
+ "authors": [
+ {
+ "name": "$AUTHOR_NAME$",
+ "email": "$AUTHOR_EMAIL$"
+ }
+ ],
+ "extra": {
+ "laravel": {
+ "providers": [],
+ "aliases": {
+
+ }
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "$MODULE_NAMESPACE$\\$STUDLY_NAME$\\": "",
+ "$MODULE_NAMESPACE$\\$STUDLY_NAME$\\App\\": "app/",
+ "$MODULE_NAMESPACE$\\$STUDLY_NAME$\\Database\\Factories\\": "database/factories/",
+ "$MODULE_NAMESPACE$\\$STUDLY_NAME$\\Database\\Seeders\\": "database/seeders/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "$MODULE_NAMESPACE$\\$STUDLY_NAME$\\Tests\\": "tests/"
+ }
+ }
+}
diff --git a/stubs/nwidart-stubs/controller-api.stub b/stubs/nwidart-stubs/controller-api.stub
new file mode 100644
index 0000000..66e70a6
--- /dev/null
+++ b/stubs/nwidart-stubs/controller-api.stub
@@ -0,0 +1,62 @@
+json($this->data);
+ }
+
+ /**
+ * Store a newly created resource in storage.
+ */
+ public function store(Request $request): JsonResponse
+ {
+ //
+
+ return response()->json($this->data);
+ }
+
+ /**
+ * Show the specified resource.
+ */
+ public function show($id): JsonResponse
+ {
+ //
+
+ return response()->json($this->data);
+ }
+
+ /**
+ * Update the specified resource in storage.
+ */
+ public function update(Request $request, $id): JsonResponse
+ {
+ //
+
+ return response()->json($this->data);
+ }
+
+ /**
+ * Remove the specified resource from storage.
+ */
+ public function destroy($id): JsonResponse
+ {
+ //
+
+ return response()->json($this->data);
+ }
+}
diff --git a/stubs/nwidart-stubs/controller-plain.stub b/stubs/nwidart-stubs/controller-plain.stub
new file mode 100644
index 0000000..662b072
--- /dev/null
+++ b/stubs/nwidart-stubs/controller-plain.stub
@@ -0,0 +1,9 @@
+get('/');
+
+ $response->assertStatus(200);
+ }
+}
diff --git a/stubs/nwidart-stubs/job-queued.stub b/stubs/nwidart-stubs/job-queued.stub
new file mode 100644
index 0000000..26dd3f0
--- /dev/null
+++ b/stubs/nwidart-stubs/job-queued.stub
@@ -0,0 +1,30 @@
+view('view.name');
+ }
+}
diff --git a/stubs/nwidart-stubs/middleware.stub b/stubs/nwidart-stubs/middleware.stub
new file mode 100644
index 0000000..bdd192d
--- /dev/null
+++ b/stubs/nwidart-stubs/middleware.stub
@@ -0,0 +1,17 @@
+id();
+ $FIELDS$
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('$TABLE$');
+ }
+};
diff --git a/stubs/nwidart-stubs/migration/delete.stub b/stubs/nwidart-stubs/migration/delete.stub
new file mode 100644
index 0000000..788cdee
--- /dev/null
+++ b/stubs/nwidart-stubs/migration/delete.stub
@@ -0,0 +1,28 @@
+id();
+ $FIELDS$
+ $table->timestamps();
+ });
+ }
+};
diff --git a/stubs/nwidart-stubs/migration/plain.stub b/stubs/nwidart-stubs/migration/plain.stub
new file mode 100644
index 0000000..88fa2f3
--- /dev/null
+++ b/stubs/nwidart-stubs/migration/plain.stub
@@ -0,0 +1,24 @@
+line('The introduction to the notification.')
+ ->action('Notification Action', 'https://laravel.com')
+ ->line('Thank you for using our application!');
+ }
+
+ /**
+ * Get the array representation of the notification.
+ */
+ public function toArray($notifiable): array
+ {
+ return [];
+ }
+}
diff --git a/stubs/nwidart-stubs/observer.stub b/stubs/nwidart-stubs/observer.stub
new file mode 100644
index 0000000..ca49863
--- /dev/null
+++ b/stubs/nwidart-stubs/observer.stub
@@ -0,0 +1,48 @@
+user()->can('users.create');
+ }
+}
diff --git a/stubs/nwidart-stubs/resource-collection.stub b/stubs/nwidart-stubs/resource-collection.stub
new file mode 100644
index 0000000..0bb408a
--- /dev/null
+++ b/stubs/nwidart-stubs/resource-collection.stub
@@ -0,0 +1,16 @@
+mapApiRoutes();
+
+ $this->mapWebRoutes();
+ }
+
+ /**
+ * Define the "web" routes for the application.
+ *
+ * These routes all receive session state, CSRF protection, etc.
+ */
+ protected function mapWebRoutes(): void
+ {
+ Route::middleware('web')
+ ->namespace($this->moduleNamespace)
+ ->group(module_path('$MODULE$', '$WEB_ROUTES_PATH$'));
+ }
+
+ /**
+ * Define the "api" routes for the application.
+ *
+ * These routes are typically stateless.
+ */
+ protected function mapApiRoutes(): void
+ {
+ Route::prefix('api')
+ ->middleware('api')
+ ->namespace($this->moduleNamespace)
+ ->group(module_path('$MODULE$', '$API_ROUTES_PATH$'));
+ }
+}
diff --git a/stubs/nwidart-stubs/routes/api.stub b/stubs/nwidart-stubs/routes/api.stub
new file mode 100644
index 0000000..10ae87e
--- /dev/null
+++ b/stubs/nwidart-stubs/routes/api.stub
@@ -0,0 +1,19 @@
+prefix('v1')->name('api.')->group(function () {
+ Route::get('$LOWER_NAME$', fn (Request $request) => $request->user())->name('$LOWER_NAME$');
+});
diff --git a/stubs/nwidart-stubs/routes/web.stub b/stubs/nwidart-stubs/routes/web.stub
new file mode 100644
index 0000000..c00f766
--- /dev/null
+++ b/stubs/nwidart-stubs/routes/web.stub
@@ -0,0 +1,19 @@
+names('$LOWER_NAME$');
+});
diff --git a/stubs/nwidart-stubs/rule.implicit.stub b/stubs/nwidart-stubs/rule.implicit.stub
new file mode 100644
index 0000000..edc029c
--- /dev/null
+++ b/stubs/nwidart-stubs/rule.implicit.stub
@@ -0,0 +1,22 @@
+ '$STUDLY_NAME$',
+];
diff --git a/stubs/nwidart-stubs/scaffold/provider.stub b/stubs/nwidart-stubs/scaffold/provider.stub
new file mode 100644
index 0000000..526c5bd
--- /dev/null
+++ b/stubs/nwidart-stubs/scaffold/provider.stub
@@ -0,0 +1,114 @@
+registerCommands();
+ $this->registerCommandSchedules();
+ $this->registerTranslations();
+ $this->registerConfig();
+ $this->registerViews();
+ $this->loadMigrationsFrom(module_path($this->moduleName, '$MIGRATIONS_PATH$'));
+ }
+
+ /**
+ * Register the service provider.
+ */
+ public function register(): void
+ {
+ $this->app->register(RouteServiceProvider::class);
+ }
+
+ /**
+ * Register commands in the format of Command::class
+ */
+ protected function registerCommands(): void
+ {
+ // $this->commands([]);
+ }
+
+ /**
+ * Register command Schedules.
+ */
+ protected function registerCommandSchedules(): void
+ {
+ // $this->app->booted(function () {
+ // $schedule = $this->app->make(Schedule::class);
+ // $schedule->command('inspire')->hourly();
+ // });
+ }
+
+ /**
+ * Register translations.
+ */
+ public function registerTranslations(): void
+ {
+ $langPath = resource_path('lang/modules/'.$this->moduleNameLower);
+
+ if (is_dir($langPath)) {
+ $this->loadTranslationsFrom($langPath, $this->moduleNameLower);
+ $this->loadJsonTranslationsFrom($langPath);
+ } else {
+ $this->loadTranslationsFrom(module_path($this->moduleName, '$PATH_LANG$'), $this->moduleNameLower);
+ $this->loadJsonTranslationsFrom(module_path($this->moduleName, '$PATH_LANG$'));
+ }
+ }
+
+ /**
+ * Register config.
+ */
+ protected function registerConfig(): void
+ {
+ $this->publishes([module_path($this->moduleName, '$PATH_CONFIG$/config.php') => config_path($this->moduleNameLower.'.php')], 'config');
+ $this->mergeConfigFrom(module_path($this->moduleName, '$PATH_CONFIG$/config.php'), $this->moduleNameLower);
+ }
+
+ /**
+ * Register views.
+ */
+ public function registerViews(): void
+ {
+ $viewPath = resource_path('views/modules/'.$this->moduleNameLower);
+ $sourcePath = module_path($this->moduleName, '$PATH_VIEWS$');
+
+ $this->publishes([$sourcePath => $viewPath], ['views', $this->moduleNameLower.'-module-views']);
+
+ $this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower);
+
+ $componentNamespace = str_replace('/', '\\', config('modules.namespace').'\\'.$this->moduleName.'\\'.config('modules.paths.generator.component-class.path'));
+ Blade::componentNamespace($componentNamespace, $this->moduleNameLower);
+ }
+
+ /**
+ * Get the services provided by the provider.
+ */
+ public function provides(): array
+ {
+ return [];
+ }
+
+ private function getPublishableViewPaths(): array
+ {
+ $paths = [];
+ foreach (config('view.paths') as $path) {
+ if (is_dir($path.'/modules/'.$this->moduleNameLower)) {
+ $paths[] = $path.'/modules/'.$this->moduleNameLower;
+ }
+ }
+
+ return $paths;
+ }
+}
diff --git a/stubs/nwidart-stubs/seeder.stub b/stubs/nwidart-stubs/seeder.stub
new file mode 100644
index 0000000..3882d1c
--- /dev/null
+++ b/stubs/nwidart-stubs/seeder.stub
@@ -0,0 +1,16 @@
+call([]);
+ }
+}
diff --git a/stubs/nwidart-stubs/unit-test.stub b/stubs/nwidart-stubs/unit-test.stub
new file mode 100644
index 0000000..be0bd55
--- /dev/null
+++ b/stubs/nwidart-stubs/unit-test.stub
@@ -0,0 +1,20 @@
+assertTrue(true);
+ }
+}
diff --git a/stubs/nwidart-stubs/views/index.stub b/stubs/nwidart-stubs/views/index.stub
new file mode 100644
index 0000000..1a535d4
--- /dev/null
+++ b/stubs/nwidart-stubs/views/index.stub
@@ -0,0 +1,7 @@
+@extends('$LOWER_NAME$::layouts.master')
+
+@section('content')
+ Hello World
+
+ Module: {!! config('$LOWER_NAME$.name') !!}
+@endsection
diff --git a/stubs/nwidart-stubs/views/master.stub b/stubs/nwidart-stubs/views/master.stub
new file mode 100644
index 0000000..8fa6ac4
--- /dev/null
+++ b/stubs/nwidart-stubs/views/master.stub
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+ $STUDLY_NAME$ Module - {{ config('app.name', 'Laravel') }}
+
+
+
+
+
+
+
+
+
+ {{-- Vite CSS --}}
+ {{-- {{ module_vite('build-$LOWER_NAME$', 'resources/assets/sass/app.scss') }} --}}
+
+
+
+ @yield('content')
+
+ {{-- Vite JS --}}
+ {{-- {{ module_vite('build-$LOWER_NAME$', 'resources/assets/js/app.js') }} --}}
+
diff --git a/stubs/nwidart-stubs/vite.stub b/stubs/nwidart-stubs/vite.stub
new file mode 100644
index 0000000..314ef81
--- /dev/null
+++ b/stubs/nwidart-stubs/vite.stub
@@ -0,0 +1,26 @@
+import { defineConfig } from 'vite';
+import laravel from 'laravel-vite-plugin';
+
+export default defineConfig({
+ build: {
+ outDir: '../../public/build-$LOWER_NAME$',
+ emptyOutDir: true,
+ manifest: true,
+ },
+ plugins: [
+ laravel({
+ publicDirectory: '../../public',
+ buildDirectory: 'build-$LOWER_NAME$',
+ input: [
+ __dirname + '/resources/assets/sass/app.scss',
+ __dirname + '/resources/assets/js/app.js'
+ ],
+ refresh: true,
+ }),
+ ],
+});
+
+//export const paths = [
+// 'Modules/$STUDLY_NAME$/resources/assets/sass/app.scss',
+// 'Modules/$STUDLY_NAME$/resources/assets/js/app.js',
+//];
\ No newline at end of file