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,
|
||||
]);
|
||||
|
||||
//
|
||||
// Register admin auth middleware
|
||||
$middleware->alias([
|
||||
'admin.auth' => \App\Http\Middleware\AdminAuth::class,
|
||||
]);
|
||||
})
|
||||
->withExceptions(function (Exceptions $exceptions): void {
|
||||
//
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
# Active Context - Current Work Status
|
||||
|
||||
## Current Task: v2.0 LARAVEL + INERTIA + VUE FOUNDATION COMPLETE ✅
|
||||
**MAD Lawsuit Website v2.0 - Complete Technology Stack Migration**:
|
||||
## Current Task: v2.0 PHASE 2 ADMIN DASHBOARD - IN PROGRESS 🚧
|
||||
**MAD Lawsuit Website v2.0 - Admin Dashboard Development**:
|
||||
- **v1.0 Status**: Fully operational at https://mad-lawsuit.org (Next.js + Express + React)
|
||||
- **v2.0 Goal**: Rebuild with Laravel + Inertia + Vue + TypeScript
|
||||
- **Current Phase**: Foundation complete, home page working, ready for features
|
||||
- **Status**: TESTED LOCALLY - WEBSITE RENDERING CORRECTLY ✅
|
||||
- **Current Phase**: Phase 2 - Admin Dashboard (50% complete)
|
||||
- **Status**: AUTHENTICATION FOUNDATION COMPLETE, NEED CONTROLLERS & VIEWS ⏳
|
||||
|
||||
### v2.0 Technology Stack
|
||||
**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
|
||||
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 () {
|
||||
return Inertia::render('Dashboard');
|
||||
})->middleware(['auth', 'verified'])->name('dashboard');
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue