Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions app/Http/Controllers/Auth/VerifyEmailController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Auth\Events\Verified;
use Illuminate\Foundation\Auth\EmailVerificationRequest;
use Illuminate\Http\RedirectResponse;

class VerifyEmailController extends Controller
{
/**
* Mark the authenticated user's email address as verified.
*/
public function __invoke(EmailVerificationRequest $request): RedirectResponse
{
if ($request->user()->hasVerifiedEmail()) {
return redirect()->intended(route('projects.index', absolute: false).'?verified=1');
}

if ($request->user()->markEmailAsVerified()) {
event(new Verified($request->user()));
}

return redirect()->intended(route('projects.index', absolute: false).'?verified=1');
}
}
4 changes: 3 additions & 1 deletion app/Http/Controllers/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

namespace App\Http\Controllers;

use Illuminate\Foundation\Auth\Access\AuthorizesRequests;

abstract class Controller
{
//
use AuthorizesRequests;
}
8 changes: 3 additions & 5 deletions app/Http/Controllers/ProjectExportController.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,19 @@ class ProjectExportController extends Controller
{
public function projectKit(Project $project, ProjectKitExporter $exporter): StreamedResponse
{
// Authorization disabled for now - add back when auth is set up
// $this->authorize('view', $project);
$this->authorize('view', $project);

$export = $exporter->export(
$project,
1 // Would be auth()->id() with real auth
auth()->id()
);

return Storage::disk($export->disk)->download($export->path, $export->filename);
}

public function tasksJson(Project $project): JsonResponse
{
// Authorization disabled for now - add back when auth is set up
// $this->authorize('view', $project);
$this->authorize('view', $project);

$project->load('tasks');

Expand Down
20 changes: 20 additions & 0 deletions app/Livewire/Actions/Logout.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace App\Livewire\Actions;

use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;

class Logout
{
/**
* Log the current user out of the application.
*/
public function __invoke(): void
{
Auth::guard('web')->logout();

Session::invalidate();
Session::regenerateToken();
}
}
72 changes: 72 additions & 0 deletions app/Livewire/Forms/LoginForm.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

namespace App\Livewire\Forms;

use Illuminate\Auth\Events\Lockout;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Livewire\Attributes\Validate;
use Livewire\Form;

class LoginForm extends Form
{
#[Validate('required|string|email')]
public string $email = '';

#[Validate('required|string')]
public string $password = '';

#[Validate('boolean')]
public bool $remember = false;

/**
* Attempt to authenticate the request's credentials.
*
* @throws \Illuminate\Validation\ValidationException
*/
public function authenticate(): void
{
$this->ensureIsNotRateLimited();

if (! Auth::attempt($this->only(['email', 'password']), $this->remember)) {
RateLimiter::hit($this->throttleKey());

throw ValidationException::withMessages([
'form.email' => trans('auth.failed'),
]);
}

RateLimiter::clear($this->throttleKey());
}

/**
* Ensure the authentication request is not rate limited.
*/
protected function ensureIsNotRateLimited(): void
{
if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
return;
}

event(new Lockout(request()));

$seconds = RateLimiter::availableIn($this->throttleKey());

throw ValidationException::withMessages([
'form.email' => trans('auth.throttle', [
'seconds' => $seconds,
'minutes' => ceil($seconds / 60),
]),
]);
}

/**
* Get the authentication rate limiting throttle key.
*/
protected function throttleKey(): string
{
return Str::transliterate(Str::lower($this->email).'|'.request()->ip());
}
}
5 changes: 2 additions & 3 deletions app/Livewire/Projects/Index.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,8 @@ public function createProject(): void

$this->validate($rules);

// For now, use user_id = 1 (test user) since we don't have auth yet
$project = Project::create([
'user_id' => 1,
'user_id' => auth()->id(),
'name' => $this->name,
'idea' => $this->idea,
'preferred_provider' => $this->selectedProvider,
Expand All @@ -64,8 +63,8 @@ public function createProject(): void

public function render()
{
// For now, show all projects (would filter by auth user later)
$projects = Project::query()
->where('user_id', auth()->id())
->where('status', ProjectStatus::Active)
->latest()
->get();
Expand Down
8 changes: 7 additions & 1 deletion app/Livewire/Projects/SettingsModal.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
use App\Enums\AiProvider;
use App\Livewire\Concerns\ManagesProviderSelection;
use App\Models\Project;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Livewire\Attributes\Computed;
use Livewire\Attributes\On;
use Livewire\Component;

class SettingsModal extends Component
{
use AuthorizesRequests;
use ManagesProviderSelection;

public bool $show = false;
Expand All @@ -20,8 +22,10 @@ class SettingsModal extends Component
#[On('openSettings')]
public function open(string $projectId): void
{
$this->projectId = $projectId;
$project = Project::findOrFail($projectId);
$this->authorize('update', $project);

$this->projectId = $projectId;

$this->initializeFromProject(
$project->preferred_provider ?? '',
Expand All @@ -42,6 +46,8 @@ public function save(): void
$this->validate($this->getProviderValidationRules());

$project = Project::findOrFail($this->projectId);
$this->authorize('update', $project);

$project->update([
'preferred_provider' => $this->selectedProvider,
'preferred_model' => $this->getFinalModel(),
Expand Down
6 changes: 6 additions & 0 deletions app/Livewire/Projects/Tabs/Export.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@

use App\Enums\DocumentType;
use App\Models\Project;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Livewire\Attributes\Computed;
use Livewire\Component;

class Export extends Component
{
use AuthorizesRequests;

public string $projectId;

public function mount(string $projectId): void
{
$project = Project::findOrFail($projectId);
$this->authorize('view', $project);

$this->projectId = $projectId;
}

Expand Down
5 changes: 5 additions & 0 deletions app/Livewire/Projects/Tabs/KanbanBoard.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use App\Models\Project;
use App\Models\Task;
use App\Models\TaskSet;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Filament\Actions\Concerns\InteractsWithActions;
use Filament\Actions\Contracts\HasActions;
use Filament\Actions\CreateAction;
Expand All @@ -29,6 +30,7 @@

class KanbanBoard extends Component implements HasActions, HasBoard, HasForms
{
use AuthorizesRequests;
use InteractsWithActions {
InteractsWithBoard::getDefaultActionRecord insteadof InteractsWithActions;
}
Expand All @@ -39,6 +41,9 @@ class KanbanBoard extends Component implements HasActions, HasBoard, HasForms

public function mount(string $projectId): void
{
$project = Project::findOrFail($projectId);
$this->authorize('view', $project);

$this->projectId = $projectId;
}

Expand Down
9 changes: 8 additions & 1 deletion app/Livewire/Projects/Tabs/Prd.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@
use App\Enums\DocumentType;
use App\Models\Document;
use App\Models\DocumentVersion;
use App\Models\Project;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Livewire\Attributes\Computed;
use Livewire\Attributes\On;
use Livewire\Component;

class Prd extends Component
{
use AuthorizesRequests;

public string $projectId;

public string $content = '';
Expand All @@ -19,6 +23,9 @@ class Prd extends Component

public function mount(string $projectId): void
{
$project = Project::findOrFail($projectId);
$this->authorize('view', $project);

$this->projectId = $projectId;
$this->loadContent();
}
Expand Down Expand Up @@ -58,7 +65,7 @@ public function save(): void
// Create a new version (append-only)
$version = DocumentVersion::create([
'document_id' => $document->id,
'created_by' => 1, // Would be auth()->id() with real auth
'created_by' => auth()->id(),
'content_md' => $this->content,
]);

Expand Down
8 changes: 7 additions & 1 deletion app/Livewire/Projects/Tabs/Tech.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@
use App\Models\Document;
use App\Models\DocumentVersion;
use App\Models\Project;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use App\Models\TaskSet;
use Livewire\Attributes\Computed;
use Livewire\Attributes\On;
use Livewire\Component;

class Tech extends Component
{
use AuthorizesRequests;

public string $projectId;

public string $content = '';
Expand All @@ -23,6 +26,9 @@ class Tech extends Component

public function mount(string $projectId): void
{
$project = Project::findOrFail($projectId);
$this->authorize('view', $project);

$this->projectId = $projectId;
$this->loadContent();
}
Expand Down Expand Up @@ -60,7 +66,7 @@ public function save(): void

$version = DocumentVersion::create([
'document_id' => $document->id,
'created_by' => 1,
'created_by' => auth()->id(),
'content_md' => $this->content,
]);

Expand Down
9 changes: 4 additions & 5 deletions app/Livewire/Projects/Workspace.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ class Workspace extends Component

public function mount(Project $project): void
{
// Authorization disabled for now - add back when auth is set up
// $this->authorize('view', $project);
$this->authorize('view', $project);
$this->projectId = $project->id;
}

Expand All @@ -49,7 +48,7 @@ public function generate(): void

$run = app(StartPlanRun::class)->handle(
$this->project,
1 // Would be auth()->id() with real auth
auth()->id()
);

$this->dispatch('planRunStarted', runId: $run->id);
Expand All @@ -61,7 +60,7 @@ public function regeneratePrd(bool $includeDownstream = false): void

$run = app(RegeneratePrd::class)->handle(
$this->project,
1, // Would be auth()->id() with real auth
auth()->id(),
$includeDownstream
);

Expand All @@ -74,7 +73,7 @@ public function regenerateTech(): void

$run = app(RegenerateTech::class)->handle(
$this->project,
1 // Would be auth()->id() with real auth
auth()->id()
);

$this->dispatch('planRunStarted', runId: $run->id);
Expand Down
Loading
Loading