Phase 2 partial: Admin authentication foundation
Authentication System Complete (50% of Phase 2): - Created Admin AuthController with login/logout - Created AdminAuth middleware for session-based auth - Registered middleware in bootstrap/app.php - Configured all admin routes with protection - Created 4 admin controller files (Dashboard, DocketEntry, Document, Subscriber) Session-based authentication: - Stores admin_id and admin_username in session - Middleware checks for admin_id presence - Separate from Laravel Breeze User auth - Uses AdminUser model with auto-hashing passwords Routes configured: - Public: GET/POST /admin/login - Protected: /admin/dashboard, /admin/docket-entries (CRUD), /admin/documents, /admin/subscribers Remaining work (50%): - Implement controller logic (7 methods for DocketEntry, etc.) - Create 9 Vue admin pages (Login, Dashboard, CRUD forms) - Create AdminSeeder for default admin user - Configure file storage for PDF uploads - Test all admin features See cline_docs/phase2_progress.md for detailed next steps
This commit is contained in:
parent
06a620406c
commit
844dcf3b73
10 changed files with 419 additions and 5 deletions
60
app/Http/Controllers/Admin/AuthController.php
Normal file
60
app/Http/Controllers/Admin/AuthController.php
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\AdminUser;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Illuminate\Support\Facades\Session;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
use Inertia\Response;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
|
||||||
|
class AuthController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Show the admin login form.
|
||||||
|
*/
|
||||||
|
public function showLogin(): Response
|
||||||
|
{
|
||||||
|
return Inertia::render('Admin/Login');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle admin login.
|
||||||
|
*/
|
||||||
|
public function login(Request $request): RedirectResponse
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'username' => 'required|string',
|
||||||
|
'password' => 'required|string',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$admin = AdminUser::where('username', $request->username)->first();
|
||||||
|
|
||||||
|
if (!$admin || !Hash::check($request->password, $admin->password)) {
|
||||||
|
return back()->withErrors([
|
||||||
|
'username' => 'The provided credentials do not match our records.',
|
||||||
|
])->onlyInput('username');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store admin ID in session
|
||||||
|
Session::put('admin_id', $admin->id);
|
||||||
|
Session::put('admin_username', $admin->username);
|
||||||
|
|
||||||
|
return redirect()->route('admin.dashboard');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle admin logout.
|
||||||
|
*/
|
||||||
|
public function logout(Request $request): RedirectResponse
|
||||||
|
{
|
||||||
|
Session::forget('admin_id');
|
||||||
|
Session::forget('admin_username');
|
||||||
|
Session::flush();
|
||||||
|
|
||||||
|
return redirect()->route('admin.login');
|
||||||
|
}
|
||||||
|
}
|
||||||
11
app/Http/Controllers/Admin/DashboardController.php
Normal file
11
app/Http/Controllers/Admin/DashboardController.php
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class DashboardController extends Controller
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
65
app/Http/Controllers/Admin/DocketEntryController.php
Normal file
65
app/Http/Controllers/Admin/DocketEntryController.php
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class DocketEntryController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for creating a new resource.
|
||||||
|
*/
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a newly created resource in storage.
|
||||||
|
*/
|
||||||
|
public function store(Request $request)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the specified resource.
|
||||||
|
*/
|
||||||
|
public function show(string $id)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for editing the specified resource.
|
||||||
|
*/
|
||||||
|
public function edit(string $id)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the specified resource in storage.
|
||||||
|
*/
|
||||||
|
public function update(Request $request, string $id)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the specified resource from storage.
|
||||||
|
*/
|
||||||
|
public function destroy(string $id)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
11
app/Http/Controllers/Admin/DocumentController.php
Normal file
11
app/Http/Controllers/Admin/DocumentController.php
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class DocumentController extends Controller
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
11
app/Http/Controllers/Admin/SubscriberController.php
Normal file
11
app/Http/Controllers/Admin/SubscriberController.php
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class SubscriberController extends Controller
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
25
app/Http/Middleware/AdminAuth.php
Normal file
25
app/Http/Middleware/AdminAuth.php
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Session;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
class AdminAuth
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
||||||
|
*/
|
||||||
|
public function handle(Request $request, Closure $next): Response
|
||||||
|
{
|
||||||
|
if (!Session::has('admin_id')) {
|
||||||
|
return redirect()->route('admin.login');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,7 +16,10 @@ return Application::configure(basePath: dirname(__DIR__))
|
||||||
\Illuminate\Http\Middleware\AddLinkHeadersForPreloadedAssets::class,
|
\Illuminate\Http\Middleware\AddLinkHeadersForPreloadedAssets::class,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
//
|
// Register admin auth middleware
|
||||||
|
$middleware->alias([
|
||||||
|
'admin.auth' => \App\Http\Middleware\AdminAuth::class,
|
||||||
|
]);
|
||||||
})
|
})
|
||||||
->withExceptions(function (Exceptions $exceptions): void {
|
->withExceptions(function (Exceptions $exceptions): void {
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
# Active Context - Current Work Status
|
# Active Context - Current Work Status
|
||||||
|
|
||||||
## Current Task: v2.0 LARAVEL + INERTIA + VUE FOUNDATION COMPLETE ✅
|
## Current Task: v2.0 PHASE 2 ADMIN DASHBOARD - IN PROGRESS 🚧
|
||||||
**MAD Lawsuit Website v2.0 - Complete Technology Stack Migration**:
|
**MAD Lawsuit Website v2.0 - Admin Dashboard Development**:
|
||||||
- **v1.0 Status**: Fully operational at https://mad-lawsuit.org (Next.js + Express + React)
|
- **v1.0 Status**: Fully operational at https://mad-lawsuit.org (Next.js + Express + React)
|
||||||
- **v2.0 Goal**: Rebuild with Laravel + Inertia + Vue + TypeScript
|
- **v2.0 Goal**: Rebuild with Laravel + Inertia + Vue + TypeScript
|
||||||
- **Current Phase**: Foundation complete, home page working, ready for features
|
- **Current Phase**: Phase 2 - Admin Dashboard (50% complete)
|
||||||
- **Status**: TESTED LOCALLY - WEBSITE RENDERING CORRECTLY ✅
|
- **Status**: AUTHENTICATION FOUNDATION COMPLETE, NEED CONTROLLERS & VIEWS ⏳
|
||||||
|
|
||||||
### v2.0 Technology Stack
|
### v2.0 Technology Stack
|
||||||
**Backend**:
|
**Backend**:
|
||||||
|
|
|
||||||
204
cline_docs/phase2_progress.md
Normal file
204
cline_docs/phase2_progress.md
Normal file
|
|
@ -0,0 +1,204 @@
|
||||||
|
# Phase 2: Admin Dashboard Progress
|
||||||
|
|
||||||
|
## Status: 50% COMPLETE - Authentication Foundation Done
|
||||||
|
|
||||||
|
### ✅ Completed Components
|
||||||
|
|
||||||
|
#### 1. Admin Authentication System
|
||||||
|
**Files Created:**
|
||||||
|
- `app/Http/Controllers/Admin/AuthController.php` - Login/logout logic
|
||||||
|
- `app/Http/Middleware/AdminAuth.php` - Session-based auth middleware
|
||||||
|
- `bootstrap/app.php` - Middleware registered as 'admin.auth'
|
||||||
|
|
||||||
|
**AuthController Methods:**
|
||||||
|
- `showLogin()` - Renders Admin/Login Inertia page
|
||||||
|
- `login()` - Validates credentials, creates session
|
||||||
|
- `logout()` - Destroys session, redirects to login
|
||||||
|
|
||||||
|
**Session Storage:**
|
||||||
|
- `admin_id` - Stored in session after successful login
|
||||||
|
- `admin_username` - Stored for display purposes
|
||||||
|
- Middleware checks for `admin_id` presence
|
||||||
|
|
||||||
|
#### 2. Admin Routes Structure
|
||||||
|
**File:** `routes/web.php`
|
||||||
|
|
||||||
|
**Public Routes (no auth):**
|
||||||
|
- `GET /admin/login` - Show login form
|
||||||
|
- `POST /admin/login` - Process login
|
||||||
|
|
||||||
|
**Protected Routes (admin.auth middleware):**
|
||||||
|
- `GET /admin/dashboard` - Main admin dashboard
|
||||||
|
- `POST /admin/logout` - Logout
|
||||||
|
- `Resource /admin/docket-entries` - Full CRUD (index, create, store, show, edit, update, destroy)
|
||||||
|
- `POST /admin/docket-entries/{id}/documents` - Upload document
|
||||||
|
- `DELETE /admin/documents/{id}` - Delete document
|
||||||
|
- `GET /admin/subscribers` - List subscribers
|
||||||
|
- `DELETE /admin/subscribers/{id}` - Delete subscriber
|
||||||
|
|
||||||
|
#### 3. Admin Controller Files Created
|
||||||
|
**All controllers created but need implementation:**
|
||||||
|
- `app/Http/Controllers/Admin/DashboardController.php` - Empty, needs index() method
|
||||||
|
- `app/Http/Controllers/Admin/DocketEntryController.php` - Resource controller, needs all 7 methods
|
||||||
|
- `app/Http/Controllers/Admin/DocumentController.php` - Empty, needs store() and destroy()
|
||||||
|
- `app/Http/Controllers/Admin/SubscriberController.php` - Empty, needs index() and destroy()
|
||||||
|
|
||||||
|
### ⏳ Remaining Work
|
||||||
|
|
||||||
|
#### 1. Implement Controller Logic (2-3 hours)
|
||||||
|
|
||||||
|
**DashboardController:**
|
||||||
|
```php
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$stats = [
|
||||||
|
'total_entries' => DocketEntry::count(),
|
||||||
|
'total_documents' => Document::count(),
|
||||||
|
'total_subscribers' => Subscription::where('is_active', true)->count(),
|
||||||
|
'recent_entries' => DocketEntry::with('documents')->latest()->take(5)->get(),
|
||||||
|
];
|
||||||
|
|
||||||
|
return Inertia::render('Admin/Dashboard', $stats);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**DocketEntryController (7 methods):**
|
||||||
|
- `index()` - List all entries with pagination
|
||||||
|
- `create()` - Show create form
|
||||||
|
- `store()` - Save new entry
|
||||||
|
- `show()` - View single entry with documents
|
||||||
|
- `edit()` - Show edit form
|
||||||
|
- `update()` - Update entry
|
||||||
|
- `destroy()` - Delete entry
|
||||||
|
|
||||||
|
**DocumentController:**
|
||||||
|
- `store()` - Upload PDF, save to storage, create DB record
|
||||||
|
- `destroy()` - Delete file from storage, remove DB record
|
||||||
|
|
||||||
|
**SubscriberController:**
|
||||||
|
- `index()` - List all subscribers with pagination
|
||||||
|
- `destroy()` - Soft delete (set is_active = false)
|
||||||
|
|
||||||
|
#### 2. Create Vue Admin Pages (2-3 hours)
|
||||||
|
|
||||||
|
**Login Page:**
|
||||||
|
- File: `resources/js/Pages/Admin/Login.vue`
|
||||||
|
- Form with username/password fields
|
||||||
|
- CSRF token handling
|
||||||
|
- Error display
|
||||||
|
- Submit to POST /admin/login
|
||||||
|
|
||||||
|
**Dashboard Page:**
|
||||||
|
- File: `resources/js/Pages/Admin/Dashboard.vue`
|
||||||
|
- Stats cards (entries, documents, subscribers)
|
||||||
|
- Recent entries list
|
||||||
|
- Quick action buttons
|
||||||
|
- Logout button
|
||||||
|
|
||||||
|
**Docket Entry Pages:**
|
||||||
|
- `resources/js/Pages/Admin/DocketEntries/Index.vue` - List with edit/delete
|
||||||
|
- `resources/js/Pages/Admin/DocketEntries/Create.vue` - Create form
|
||||||
|
- `resources/js/Pages/Admin/DocketEntries/Edit.vue` - Edit form
|
||||||
|
- `resources/js/Pages/Admin/DocketEntries/Show.vue` - View with document upload
|
||||||
|
|
||||||
|
**Subscriber Page:**
|
||||||
|
- `resources/js/Pages/Admin/Subscribers/Index.vue` - List with delete
|
||||||
|
|
||||||
|
#### 3. Create Admin Seeder (15 min)
|
||||||
|
|
||||||
|
**File:** `database/seeders/AdminSeeder.php`
|
||||||
|
```php
|
||||||
|
AdminUser::create([
|
||||||
|
'username' => 'admin',
|
||||||
|
'password' => 'password', // Will be auto-hashed by model
|
||||||
|
]);
|
||||||
|
```
|
||||||
|
|
||||||
|
Run: `php artisan db:seed --class=AdminSeeder`
|
||||||
|
|
||||||
|
#### 4. File Storage Configuration (15 min)
|
||||||
|
|
||||||
|
**Update:** `config/filesystems.php`
|
||||||
|
- Ensure 'public' disk is configured
|
||||||
|
- Create storage link: `php artisan storage:link`
|
||||||
|
- Documents will be stored in `storage/app/public/documents/`
|
||||||
|
|
||||||
|
### Next Session Checklist
|
||||||
|
|
||||||
|
**Start Here:**
|
||||||
|
1. ✅ Read this file first
|
||||||
|
2. ✅ Review `routes/web.php` to understand route structure
|
||||||
|
3. ✅ Review `app/Http/Controllers/Admin/AuthController.php` for auth pattern
|
||||||
|
4. Implement DashboardController->index()
|
||||||
|
5. Create Admin/Dashboard.vue page
|
||||||
|
6. Test admin login flow
|
||||||
|
7. Implement DocketEntryController methods
|
||||||
|
8. Create docket entry Vue pages
|
||||||
|
9. Implement DocumentController methods
|
||||||
|
10. Implement SubscriberController methods
|
||||||
|
11. Create AdminSeeder
|
||||||
|
12. Test all admin features
|
||||||
|
13. Commit Phase 2 complete
|
||||||
|
|
||||||
|
### Key Design Decisions
|
||||||
|
|
||||||
|
**Authentication:**
|
||||||
|
- Session-based (not token-based)
|
||||||
|
- Separate from Laravel Breeze User auth
|
||||||
|
- AdminUser model with password hashing
|
||||||
|
- Middleware checks session for admin_id
|
||||||
|
|
||||||
|
**File Storage:**
|
||||||
|
- PDFs stored in `storage/app/public/documents/`
|
||||||
|
- Accessible via `/storage/documents/{filename}`
|
||||||
|
- Original filename preserved in DB
|
||||||
|
- Unique stored filename to prevent conflicts
|
||||||
|
|
||||||
|
**Admin UI:**
|
||||||
|
- Inertia.js for seamless SPA experience
|
||||||
|
- Vue 3 + TypeScript
|
||||||
|
- Tailwind CSS for styling
|
||||||
|
- Match v1.0 admin dashboard design
|
||||||
|
|
||||||
|
### Testing Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start dev servers
|
||||||
|
php artisan serve
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# Access admin
|
||||||
|
http://127.0.0.1:8000/admin/login
|
||||||
|
|
||||||
|
# Create admin user
|
||||||
|
php artisan db:seed --class=AdminSeeder
|
||||||
|
|
||||||
|
# Test login
|
||||||
|
Username: admin
|
||||||
|
Password: password
|
||||||
|
```
|
||||||
|
|
||||||
|
### Files to Create Next Session
|
||||||
|
|
||||||
|
1. `app/Http/Controllers/Admin/DashboardController.php` - Implement index()
|
||||||
|
2. `resources/js/Pages/Admin/Login.vue`
|
||||||
|
3. `resources/js/Pages/Admin/Dashboard.vue`
|
||||||
|
4. `resources/js/Pages/Admin/DocketEntries/Index.vue`
|
||||||
|
5. `resources/js/Pages/Admin/DocketEntries/Create.vue`
|
||||||
|
6. `resources/js/Pages/Admin/DocketEntries/Edit.vue`
|
||||||
|
7. `resources/js/Pages/Admin/DocketEntries/Show.vue`
|
||||||
|
8. `resources/js/Pages/Admin/Subscribers/Index.vue`
|
||||||
|
9. `database/seeders/AdminSeeder.php`
|
||||||
|
|
||||||
|
### Estimated Time Remaining
|
||||||
|
|
||||||
|
- Controller implementation: 2-3 hours
|
||||||
|
- Vue pages: 2-3 hours
|
||||||
|
- Testing & fixes: 1 hour
|
||||||
|
- **Total: 5-7 hours**
|
||||||
|
|
||||||
|
### Current Git Status
|
||||||
|
|
||||||
|
- Phase 1 committed and pushed ✅
|
||||||
|
- Phase 2 authentication foundation ready to commit
|
||||||
|
- Next commit will be "Phase 2 partial: Admin authentication foundation"
|
||||||
|
|
@ -18,6 +18,30 @@ Route::get('/api/unsubscribe/{token}', [SubscriptionController::class, 'unsubscr
|
||||||
// Document download route
|
// Document download route
|
||||||
Route::get('/api/documents/{id}/download', [DocumentController::class, 'download'])->name('api.documents.download');
|
Route::get('/api/documents/{id}/download', [DocumentController::class, 'download'])->name('api.documents.download');
|
||||||
|
|
||||||
|
// Admin routes
|
||||||
|
Route::prefix('admin')->name('admin.')->group(function () {
|
||||||
|
// Admin login routes (no auth required)
|
||||||
|
Route::get('/login', [\App\Http\Controllers\Admin\AuthController::class, 'showLogin'])->name('login');
|
||||||
|
Route::post('/login', [\App\Http\Controllers\Admin\AuthController::class, 'login'])->name('login.post');
|
||||||
|
|
||||||
|
// Protected admin routes
|
||||||
|
Route::middleware('admin.auth')->group(function () {
|
||||||
|
Route::get('/dashboard', [\App\Http\Controllers\Admin\DashboardController::class, 'index'])->name('dashboard');
|
||||||
|
Route::post('/logout', [\App\Http\Controllers\Admin\AuthController::class, 'logout'])->name('logout');
|
||||||
|
|
||||||
|
// Docket entry routes
|
||||||
|
Route::resource('docket-entries', \App\Http\Controllers\Admin\DocketEntryController::class);
|
||||||
|
|
||||||
|
// Document routes
|
||||||
|
Route::post('/docket-entries/{docketEntry}/documents', [\App\Http\Controllers\Admin\DocumentController::class, 'store'])->name('documents.store');
|
||||||
|
Route::delete('/documents/{document}', [\App\Http\Controllers\Admin\DocumentController::class, 'destroy'])->name('documents.destroy');
|
||||||
|
|
||||||
|
// Subscriber routes
|
||||||
|
Route::get('/subscribers', [\App\Http\Controllers\Admin\SubscriberController::class, 'index'])->name('subscribers.index');
|
||||||
|
Route::delete('/subscribers/{subscription}', [\App\Http\Controllers\Admin\SubscriberController::class, 'destroy'])->name('subscribers.destroy');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
Route::get('/dashboard', function () {
|
Route::get('/dashboard', function () {
|
||||||
return Inertia::render('Dashboard');
|
return Inertia::render('Dashboard');
|
||||||
})->middleware(['auth', 'verified'])->name('dashboard');
|
})->middleware(['auth', 'verified'])->name('dashboard');
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue