docs: consolidate documentation into organized structure

- Move 12 markdown files from root to docs/ subdirectories
- Organize documentation by category:
  • docs/troubleshooting/ (1 file)  - Technical troubleshooting guides
  • docs/deployment/      (4 files) - Deployment and security documentation
  • docs/guides/          (3 files) - Feature-specific guides
  • docs/planning/        (4 files) - Planning and improvement proposals

Root directory cleanup:
- Reduced from 16 to 4 markdown files in root
- Only essential project files remain:
  • CLAUDE.md (AI instructions)
  • README.md (Main project readme)
  • CLEANUP_PLAN.md (Current cleanup plan)
  • SRC_STRUCTURE_IMPROVEMENTS.md (Structure improvements)

This improves:
 Documentation discoverability
 Logical organization by purpose
 Clean root directory
 Better maintainability
This commit is contained in:
2025-10-05 11:05:04 +02:00
parent 887847dde6
commit 5050c7d73a
36686 changed files with 196456 additions and 12398919 deletions

View File

@@ -2,21 +2,21 @@
declare(strict_types=1);
namespace App\Framework\Smartlinks\Actions;
namespace App\Framework\MagicLinks\Actions;
use App\Framework\Smartlinks\TokenAction;
use App\Framework\MagicLinks\TokenAction;
interface ActionRegistry
{
/**
* Register an action
*/
public function register(SmartlinkAction $action): void;
public function register(MagicLinkAction $action): void;
/**
* Get an action by name
*/
public function get(TokenAction $action): ?SmartlinkAction;
public function get(TokenAction $action): ?MagicLinkAction;
/**
* Check if an action is registered

View File

@@ -2,35 +2,46 @@
declare(strict_types=1);
namespace App\Framework\Smartlinks\Actions;
namespace App\Framework\MagicLinks\Actions;
use App\Framework\MagicLinks\ValueObjects\ActionResultData;
use App\Framework\MagicLinks\ValueObjects\ErrorCollection;
final readonly class ActionResult
{
public function __construct(
public bool $success,
public string $message = '',
public array $data = [],
public ActionResultData $data = new ActionResultData([]),
public ?string $redirectUrl = null,
public array $errors = []
public ErrorCollection $errors = new ErrorCollection([])
) {
}
public static function success(string $message = '', array $data = [], ?string $redirectUrl = null): self
{
public static function success(
string $message = '',
?ActionResultData $data = null,
?string $redirectUrl = null
): self {
return new self(
success: true,
message: $message,
data: $data,
redirectUrl: $redirectUrl
data: $data ?? ActionResultData::empty(),
redirectUrl: $redirectUrl,
errors: ErrorCollection::empty()
);
}
public static function failure(string $message, array $errors = []): self
{
public static function failure(
string $message,
?ErrorCollection $errors = null
): self {
return new self(
success: false,
message: $message,
errors: $errors
data: ActionResultData::empty(),
redirectUrl: null,
errors: $errors ?? ErrorCollection::empty()
);
}
@@ -43,4 +54,9 @@ final readonly class ActionResult
{
return $this->redirectUrl !== null;
}
public function hasErrors(): bool
{
return $this->errors->hasErrors();
}
}

View File

@@ -2,21 +2,21 @@
declare(strict_types=1);
namespace App\Framework\Smartlinks\Actions;
namespace App\Framework\MagicLinks\Actions;
use App\Framework\Smartlinks\TokenAction;
use App\Framework\MagicLinks\TokenAction;
final class DefaultActionRegistry implements ActionRegistry
{
/** @var SmartlinkAction[] */
/** @var MagicLinkAction[] */
private array $actions = [];
public function register(SmartlinkAction $action): void
public function register(MagicLinkAction $action): void
{
$this->actions[$action->getName()] = $action;
}
public function get(TokenAction $action): ?SmartlinkAction
public function get(TokenAction $action): ?MagicLinkAction
{
return $this->actions[$action->name] ?? null;
}

View File

@@ -2,12 +2,15 @@
declare(strict_types=1);
namespace App\Framework\Smartlinks\Actions;
namespace App\Framework\MagicLinks\Actions;
use App\Framework\Smartlinks\SmartlinkData;
use App\Framework\Smartlinks\TokenConfig;
use App\Framework\Http\ValueObjects\IpPatternCollection;
use App\Framework\MagicLinks\MagicLinkData;
use App\Framework\MagicLinks\TokenConfig;
use App\Framework\MagicLinks\ValueObjects\ActionResultData;
use App\Framework\MagicLinks\ValueObjects\Metadata;
final readonly class DocumentAccessAction implements SmartlinkAction
final readonly class DocumentAccessAction implements MagicLinkAction
{
public function getName(): string
{
@@ -20,7 +23,9 @@ final readonly class DocumentAccessAction implements SmartlinkAction
expiryHours: 72, // 3 days
oneTimeUse: false, // Can be accessed multiple times
maxUses: 10,
requireSecureContext: false
requireSecureContext: false,
allowedIpRanges: IpPatternCollection::empty(),
metadata: Metadata::empty()
);
}
@@ -30,19 +35,19 @@ final readonly class DocumentAccessAction implements SmartlinkAction
in_array($payload['access_level'], ['read', 'download', 'edit']);
}
public function execute(SmartlinkData $smartlinkData, array $context = []): ActionResult
public function execute(MagicLinkData $magiclinkData, array $context = []): ActionResult
{
$payload = $smartlinkData->payload;
$payload = $magiclinkData->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'),
]
data: ActionResultData::fromArray([
'document_id' => $payload->get('document_id'),
'access_level' => $payload->get('access_level'),
'download_url' => "/documents/{$payload->get('document_id')}/download",
'expires_at' => $magiclinkData->expiresAt->format('Y-m-d H:i:s'),
])
);
}

View File

@@ -2,13 +2,16 @@
declare(strict_types=1);
namespace App\Framework\Smartlinks\Actions;
namespace App\Framework\MagicLinks\Actions;
use App\Framework\Smartlinks\SmartlinkData;
use App\Framework\Smartlinks\TokenConfig;
use App\Framework\Http\ValueObjects\IpPatternCollection;
use App\Framework\MagicLinks\MagicLinkData;
use App\Framework\MagicLinks\TokenConfig;
use App\Framework\MagicLinks\ValueObjects\ActionResultData;
use App\Framework\MagicLinks\ValueObjects\Metadata;
use DateTimeImmutable;
final readonly class EmailVerificationAction implements SmartlinkAction
final readonly class EmailVerificationAction implements MagicLinkAction
{
public function getName(): string
{
@@ -21,7 +24,9 @@ final readonly class EmailVerificationAction implements SmartlinkAction
expiryHours: 24,
oneTimeUse: true,
maxUses: 1,
requireSecureContext: true
requireSecureContext: true,
allowedIpRanges: IpPatternCollection::empty(),
metadata: Metadata::empty()
);
}
@@ -32,20 +37,20 @@ final readonly class EmailVerificationAction implements SmartlinkAction
filter_var($payload['email'], FILTER_VALIDATE_EMAIL);
}
public function execute(SmartlinkData $smartlinkData, array $context = []): ActionResult
public function execute(MagicLinkData $magiclinkData, array $context = []): ActionResult
{
$payload = $smartlinkData->payload;
$payload = $magiclinkData->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'],
message: "Email {$payload->get('email')} successfully verified",
data: ActionResultData::fromArray([
'user_id' => $payload->get('user_id'),
'email' => $payload->get('email'),
'verified_at' => new DateTimeImmutable(),
],
]),
redirectUrl: '/dashboard'
);
}

View File

@@ -2,12 +2,13 @@
declare(strict_types=1);
namespace App\Framework\Smartlinks\Actions;
namespace App\Framework\MagicLinks\Actions;
use App\Framework\Smartlinks\SmartlinkData;
use App\Framework\Smartlinks\TokenConfig;
use App\Framework\MagicLinks\MagicLinkData;
use App\Framework\MagicLinks\TokenConfig;
use App\Framework\MagicLinks\ValueObjects\ActionResultData;
final readonly class GenericDataAccessAction implements SmartlinkAction
final readonly class GenericDataAccessAction implements MagicLinkAction
{
public function __construct(
private string $actionName,
@@ -32,12 +33,12 @@ final readonly class GenericDataAccessAction implements SmartlinkAction
return ! empty($payload);
}
public function execute(SmartlinkData $smartlinkData, array $context = []): ActionResult
public function execute(MagicLinkData $magiclinkData, array $context = []): ActionResult
{
// Generic execution - just return the payload data
return ActionResult::success(
message: "Action '{$this->actionName}' executed successfully",
data: $smartlinkData->payload
data: ActionResultData::fromArray($magiclinkData->payload->toArray())
);
}

View File

@@ -2,12 +2,12 @@
declare(strict_types=1);
namespace App\Framework\Smartlinks\Actions;
namespace App\Framework\MagicLinks\Actions;
use App\Framework\Smartlinks\SmartlinkData;
use App\Framework\Smartlinks\TokenConfig;
use App\Framework\MagicLinks\MagicLinkData;
use App\Framework\MagicLinks\TokenConfig;
interface SmartlinkAction
interface MagicLinkAction
{
/**
* Get the action name
@@ -27,7 +27,7 @@ interface SmartlinkAction
/**
* Execute the action
*/
public function execute(SmartlinkData $smartlinkData, array $context = []): ActionResult;
public function execute(MagicLinkData $magiclinkData, array $context = []): ActionResult;
/**
* Get required permissions/roles for this action

View File

@@ -2,12 +2,15 @@
declare(strict_types=1);
namespace App\Framework\Smartlinks\Actions;
namespace App\Framework\MagicLinks\Actions;
use App\Framework\Smartlinks\SmartlinkData;
use App\Framework\Smartlinks\TokenConfig;
use App\Framework\Http\ValueObjects\IpPatternCollection;
use App\Framework\MagicLinks\MagicLinkData;
use App\Framework\MagicLinks\TokenConfig;
use App\Framework\MagicLinks\ValueObjects\ActionResultData;
use App\Framework\MagicLinks\ValueObjects\Metadata;
final readonly class PasswordResetAction implements SmartlinkAction
final readonly class PasswordResetAction implements MagicLinkAction
{
public function getName(): string
{
@@ -20,7 +23,9 @@ final readonly class PasswordResetAction implements SmartlinkAction
expiryHours: 1, // Short expiry for security
oneTimeUse: true,
maxUses: 1,
requireSecureContext: true
requireSecureContext: true,
allowedIpRanges: IpPatternCollection::empty(),
metadata: Metadata::empty()
);
}
@@ -31,21 +36,21 @@ final readonly class PasswordResetAction implements SmartlinkAction
filter_var($payload['email'], FILTER_VALIDATE_EMAIL);
}
public function execute(SmartlinkData $smartlinkData, array $context = []): ActionResult
public function execute(MagicLinkData $magiclinkData, array $context = []): ActionResult
{
$payload = $smartlinkData->payload;
$payload = $magiclinkData->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'],
data: ActionResultData::fromArray([
'user_id' => $payload->get('user_id'),
'email' => $payload->get('email'),
'form_action' => '/password/reset/submit',
'csrf_token' => $context['csrf_token'] ?? null,
]
])
);
}