refactor: reorganize project structure for better maintainability
- Move 45 debug/test files from root to organized scripts/ directories - Secure public/ directory by removing debug files (security improvement) - Create structured scripts organization: • scripts/debug/ (20 files) - Framework debugging tools • scripts/test/ (18 files) - Test and validation scripts • scripts/maintenance/ (5 files) - Maintenance utilities • scripts/dev/ (2 files) - Development tools Security improvements: - Removed all debug/test files from public/ directory - Only production files remain: index.php, health.php Root directory cleanup: - Reduced from 47 to 2 PHP files in root - Only essential production files: console.php, worker.php This improves: ✅ Security (no debug code in public/) ✅ Organization (clear separation of concerns) ✅ Maintainability (easy to find and manage scripts) ✅ Professional structure (clean root directory)
This commit is contained in:
35
src/Framework/MagicLinks/Actions/ActionRegistry.php
Normal file
35
src/Framework/MagicLinks/Actions/ActionRegistry.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Smartlinks\Actions;
|
||||
|
||||
use App\Framework\Smartlinks\TokenAction;
|
||||
|
||||
interface ActionRegistry
|
||||
{
|
||||
/**
|
||||
* Register an action
|
||||
*/
|
||||
public function register(SmartlinkAction $action): void;
|
||||
|
||||
/**
|
||||
* Get an action by name
|
||||
*/
|
||||
public function get(TokenAction $action): ?SmartlinkAction;
|
||||
|
||||
/**
|
||||
* Check if an action is registered
|
||||
*/
|
||||
public function has(TokenAction $action): bool;
|
||||
|
||||
/**
|
||||
* Get all registered actions
|
||||
*/
|
||||
public function getAll(): array;
|
||||
|
||||
/**
|
||||
* Unregister an action
|
||||
*/
|
||||
public function unregister(TokenAction $action): void;
|
||||
}
|
||||
46
src/Framework/MagicLinks/Actions/ActionResult.php
Normal file
46
src/Framework/MagicLinks/Actions/ActionResult.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Smartlinks\Actions;
|
||||
|
||||
final readonly class ActionResult
|
||||
{
|
||||
public function __construct(
|
||||
public bool $success,
|
||||
public string $message = '',
|
||||
public array $data = [],
|
||||
public ?string $redirectUrl = null,
|
||||
public array $errors = []
|
||||
) {
|
||||
}
|
||||
|
||||
public static function success(string $message = '', array $data = [], ?string $redirectUrl = null): self
|
||||
{
|
||||
return new self(
|
||||
success: true,
|
||||
message: $message,
|
||||
data: $data,
|
||||
redirectUrl: $redirectUrl
|
||||
);
|
||||
}
|
||||
|
||||
public static function failure(string $message, array $errors = []): self
|
||||
{
|
||||
return new self(
|
||||
success: false,
|
||||
message: $message,
|
||||
errors: $errors
|
||||
);
|
||||
}
|
||||
|
||||
public function isSuccess(): bool
|
||||
{
|
||||
return $this->success;
|
||||
}
|
||||
|
||||
public function hasRedirect(): bool
|
||||
{
|
||||
return $this->redirectUrl !== null;
|
||||
}
|
||||
}
|
||||
38
src/Framework/MagicLinks/Actions/DefaultActionRegistry.php
Normal file
38
src/Framework/MagicLinks/Actions/DefaultActionRegistry.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Smartlinks\Actions;
|
||||
|
||||
use App\Framework\Smartlinks\TokenAction;
|
||||
|
||||
final class DefaultActionRegistry implements ActionRegistry
|
||||
{
|
||||
/** @var SmartlinkAction[] */
|
||||
private array $actions = [];
|
||||
|
||||
public function register(SmartlinkAction $action): void
|
||||
{
|
||||
$this->actions[$action->getName()] = $action;
|
||||
}
|
||||
|
||||
public function get(TokenAction $action): ?SmartlinkAction
|
||||
{
|
||||
return $this->actions[$action->name] ?? null;
|
||||
}
|
||||
|
||||
public function has(TokenAction $action): bool
|
||||
{
|
||||
return isset($this->actions[$action->name]);
|
||||
}
|
||||
|
||||
public function getAll(): array
|
||||
{
|
||||
return $this->actions;
|
||||
}
|
||||
|
||||
public function unregister(TokenAction $action): void
|
||||
{
|
||||
unset($this->actions[$action->name]);
|
||||
}
|
||||
}
|
||||
53
src/Framework/MagicLinks/Actions/DocumentAccessAction.php
Normal file
53
src/Framework/MagicLinks/Actions/DocumentAccessAction.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Smartlinks\Actions;
|
||||
|
||||
use App\Framework\Smartlinks\SmartlinkData;
|
||||
use App\Framework\Smartlinks\TokenConfig;
|
||||
|
||||
final readonly class DocumentAccessAction implements SmartlinkAction
|
||||
{
|
||||
public function getName(): string
|
||||
{
|
||||
return 'document_access';
|
||||
}
|
||||
|
||||
public function getDefaultConfig(): TokenConfig
|
||||
{
|
||||
return new TokenConfig(
|
||||
expiryHours: 72, // 3 days
|
||||
oneTimeUse: false, // Can be accessed multiple times
|
||||
maxUses: 10,
|
||||
requireSecureContext: false
|
||||
);
|
||||
}
|
||||
|
||||
public function validatePayload(array $payload): bool
|
||||
{
|
||||
return ! empty($payload['document_id']) &&
|
||||
in_array($payload['access_level'], ['read', 'download', 'edit']);
|
||||
}
|
||||
|
||||
public function execute(SmartlinkData $smartlinkData, array $context = []): ActionResult
|
||||
{
|
||||
$payload = $smartlinkData->payload;
|
||||
|
||||
// Document access logic
|
||||
return ActionResult::success(
|
||||
message: "Document access granted",
|
||||
data: [
|
||||
'document_id' => $payload['document_id'],
|
||||
'access_level' => $payload['access_level'],
|
||||
'download_url' => "/documents/{$payload['document_id']}/download",
|
||||
'expires_at' => $smartlinkData->expiresAt->format('Y-m-d H:i:s'),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getRequiredPermissions(): array
|
||||
{
|
||||
return ['documents.access'];
|
||||
}
|
||||
}
|
||||
57
src/Framework/MagicLinks/Actions/EmailVerificationAction.php
Normal file
57
src/Framework/MagicLinks/Actions/EmailVerificationAction.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Smartlinks\Actions;
|
||||
|
||||
use App\Framework\Smartlinks\SmartlinkData;
|
||||
use App\Framework\Smartlinks\TokenConfig;
|
||||
use DateTimeImmutable;
|
||||
|
||||
final readonly class EmailVerificationAction implements SmartlinkAction
|
||||
{
|
||||
public function getName(): string
|
||||
{
|
||||
return 'email_verification';
|
||||
}
|
||||
|
||||
public function getDefaultConfig(): TokenConfig
|
||||
{
|
||||
return new TokenConfig(
|
||||
expiryHours: 24,
|
||||
oneTimeUse: true,
|
||||
maxUses: 1,
|
||||
requireSecureContext: true
|
||||
);
|
||||
}
|
||||
|
||||
public function validatePayload(array $payload): bool
|
||||
{
|
||||
return ! empty($payload['email']) &&
|
||||
! empty($payload['user_id']) &&
|
||||
filter_var($payload['email'], FILTER_VALIDATE_EMAIL);
|
||||
}
|
||||
|
||||
public function execute(SmartlinkData $smartlinkData, array $context = []): ActionResult
|
||||
{
|
||||
$payload = $smartlinkData->payload;
|
||||
|
||||
// Email verification logic would go here
|
||||
// For now, just a simple success response
|
||||
|
||||
return ActionResult::success(
|
||||
message: "Email {$payload['email']} successfully verified",
|
||||
data: [
|
||||
'user_id' => $payload['user_id'],
|
||||
'email' => $payload['email'],
|
||||
'verified_at' => new DateTimeImmutable(),
|
||||
],
|
||||
redirectUrl: '/dashboard'
|
||||
);
|
||||
}
|
||||
|
||||
public function getRequiredPermissions(): array
|
||||
{
|
||||
return []; // No special permissions needed for email verification
|
||||
}
|
||||
}
|
||||
48
src/Framework/MagicLinks/Actions/GenericDataAccessAction.php
Normal file
48
src/Framework/MagicLinks/Actions/GenericDataAccessAction.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Smartlinks\Actions;
|
||||
|
||||
use App\Framework\Smartlinks\SmartlinkData;
|
||||
use App\Framework\Smartlinks\TokenConfig;
|
||||
|
||||
final readonly class GenericDataAccessAction implements SmartlinkAction
|
||||
{
|
||||
public function __construct(
|
||||
private string $actionName,
|
||||
private TokenConfig $config,
|
||||
private array $requiredPermissions = []
|
||||
) {
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->actionName;
|
||||
}
|
||||
|
||||
public function getDefaultConfig(): TokenConfig
|
||||
{
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
public function validatePayload(array $payload): bool
|
||||
{
|
||||
// Generic validation - just check that payload is not empty
|
||||
return ! empty($payload);
|
||||
}
|
||||
|
||||
public function execute(SmartlinkData $smartlinkData, array $context = []): ActionResult
|
||||
{
|
||||
// Generic execution - just return the payload data
|
||||
return ActionResult::success(
|
||||
message: "Action '{$this->actionName}' executed successfully",
|
||||
data: $smartlinkData->payload
|
||||
);
|
||||
}
|
||||
|
||||
public function getRequiredPermissions(): array
|
||||
{
|
||||
return $this->requiredPermissions;
|
||||
}
|
||||
}
|
||||
36
src/Framework/MagicLinks/Actions/MagicLinkAction.php
Normal file
36
src/Framework/MagicLinks/Actions/MagicLinkAction.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Smartlinks\Actions;
|
||||
|
||||
use App\Framework\Smartlinks\SmartlinkData;
|
||||
use App\Framework\Smartlinks\TokenConfig;
|
||||
|
||||
interface SmartlinkAction
|
||||
{
|
||||
/**
|
||||
* Get the action name
|
||||
*/
|
||||
public function getName(): string;
|
||||
|
||||
/**
|
||||
* Get default configuration for this action
|
||||
*/
|
||||
public function getDefaultConfig(): TokenConfig;
|
||||
|
||||
/**
|
||||
* Validate the payload for this action
|
||||
*/
|
||||
public function validatePayload(array $payload): bool;
|
||||
|
||||
/**
|
||||
* Execute the action
|
||||
*/
|
||||
public function execute(SmartlinkData $smartlinkData, array $context = []): ActionResult;
|
||||
|
||||
/**
|
||||
* Get required permissions/roles for this action
|
||||
*/
|
||||
public function getRequiredPermissions(): array;
|
||||
}
|
||||
56
src/Framework/MagicLinks/Actions/PasswordResetAction.php
Normal file
56
src/Framework/MagicLinks/Actions/PasswordResetAction.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Smartlinks\Actions;
|
||||
|
||||
use App\Framework\Smartlinks\SmartlinkData;
|
||||
use App\Framework\Smartlinks\TokenConfig;
|
||||
|
||||
final readonly class PasswordResetAction implements SmartlinkAction
|
||||
{
|
||||
public function getName(): string
|
||||
{
|
||||
return 'password_reset';
|
||||
}
|
||||
|
||||
public function getDefaultConfig(): TokenConfig
|
||||
{
|
||||
return new TokenConfig(
|
||||
expiryHours: 1, // Short expiry for security
|
||||
oneTimeUse: true,
|
||||
maxUses: 1,
|
||||
requireSecureContext: true
|
||||
);
|
||||
}
|
||||
|
||||
public function validatePayload(array $payload): bool
|
||||
{
|
||||
return ! empty($payload['user_id']) &&
|
||||
! empty($payload['email']) &&
|
||||
filter_var($payload['email'], FILTER_VALIDATE_EMAIL);
|
||||
}
|
||||
|
||||
public function execute(SmartlinkData $smartlinkData, array $context = []): ActionResult
|
||||
{
|
||||
$payload = $smartlinkData->payload;
|
||||
|
||||
// Password reset form would be shown here
|
||||
// Return data needed for the password reset form
|
||||
|
||||
return ActionResult::success(
|
||||
message: "Password reset form ready",
|
||||
data: [
|
||||
'user_id' => $payload['user_id'],
|
||||
'email' => $payload['email'],
|
||||
'form_action' => '/password/reset/submit',
|
||||
'csrf_token' => $context['csrf_token'] ?? null,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getRequiredPermissions(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user