Skip to content

Commit 323e67f

Browse files
TatevikGrtatevikg1
andauthored
Insert initial admin command (#368)
* Update: codeRabbit configs * Update: Use command for initial admin insert --------- Co-authored-by: Tatevik <[email protected]>
1 parent 5231d0a commit 323e67f

File tree

5 files changed

+246
-57
lines changed

5 files changed

+246
-57
lines changed

.coderabbit.yaml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,50 @@
1+
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
12
language: "en-US"
3+
tone_instructions: "chill"
24
reviews:
35
profile: "chill"
46
high_level_summary: true
7+
collapse_walkthrough: true
8+
suggested_labels: false
9+
high_level_summary_in_walkthrough: false
10+
poem: false
11+
path_instructions:
12+
- path: "src/Domain/**"
13+
instructions: |
14+
You are reviewing PHP domain-layer code. Enforce strict domain purity:
15+
- ❌ Do not allow infrastructure persistence side effects here.
16+
- Flag ANY usage of Doctrine persistence APIs, especially:
17+
- $entityManager->flush(...), $this->entityManager->flush(...)
18+
- $em->persist(...), $em->remove(...)
19+
- direct transaction control ($em->beginTransaction(), commit(), rollback())
20+
- If found, request moving these calls to application-layer Command handlers or background Jobs.
21+
- Also flag repositories in Domain that invoke flush/transactional logic; Domain repositories should be abstractions without side effects.
22+
- Encourage domain events/outbox or return-values to signal write-intent, leaving orchestration to Commands/Jobs.
23+
24+
- path: "src/**/Command/**"
25+
instructions: |
26+
Application layer (Commands/Handlers) is the right place to coordinate persistence.
27+
- ✅ It is acceptable to call $entityManager->flush() here.
28+
- Check that flush is used atomically (once per unit of work) after all domain operations.
29+
- Ensure no domain entity or domain service is calling flush; only the handler orchestrates it.
30+
- Prefer $em->transactional(...) or explicit try/catch with rollback on failure.
31+
32+
- path: "src/**/MessageHandler/**"
33+
instructions: |
34+
Background jobs/workers may perform persistence.
35+
- ✅ Allow $entityManager->flush() here when the job is the orchestration boundary.
36+
- Verify idempotency and that flush frequency is appropriate (batching where practical).
37+
- Ensure no domain-layer code invoked by the job performs flush/transaction control.
38+
539
auto_review:
640
enabled: true
741
base_branches:
842
- ".*"
943
drafts: false
44+
# ignore_title_keywords:
45+
# - ''
46+
47+
#knowledge_base:
48+
# code_guidelines:
49+
# filePatterns:
50+
# - ".github/AGENT.md"

.github/AGENT.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# AGENT.md — Code Review Knowledge Base for phpList/core
2+
3+
## 🧭 Repository Overview
4+
5+
This repository is the **core package** of **phpList 4**, a modular and extensible email campaign management system.
6+
7+
- **Purpose:** Provides the reusable foundation and framework for phpList applications and modules.
8+
- **Consumers:** `phpList/web-frontend`, `phpList/rest-api`, and `phpList/base-distribution`.
9+
- **Core responsibilities:**
10+
- Application bootstrapping and service configuration
11+
- Database ORM (Doctrine) integration
12+
- Command-line utilities (Symfony Console)
13+
- Async email sending via Symfony Messenger
14+
- Logging, configuration, routing, dependency injection
15+
- Schema definition and updates
16+
17+
> **Note:** This repository does *not* contain UI or REST endpoints. Those are part of other phpList packages.
18+
19+
---
20+
21+
## ⚙️ Tech Stack
22+
23+
| Category | Technology |
24+
|-----------|-------------|
25+
| Language | PHP ≥ 8.1 |
26+
| Framework | Symfony 6.x components |
27+
| ORM | Doctrine ORM 3.x |
28+
| Async / Queue | Symfony Messenger |
29+
| Tests | PHPUnit |
30+
| Static analysis | PHPStan |
31+
| Docs | PHPDocumentor |
32+
| License | AGPL-3.0 |
33+
34+
---
35+
36+
## 📁 Project Structure
37+
- .github/ CI/CD and PR templates
38+
- bin/ Symfony console entrypoints
39+
- config/ Application & environment configs
40+
- docs/ Developer documentation and generated API docs
41+
- public/ Public entrypoint for local web server
42+
- resources/Database/ Canonical SQL schema
43+
- src/ Core PHP source code
44+
- tests/ PHPUnit test suites
45+
- composer.json Package metadata and dependencies
46+
- phpunit.xml.dist Test configuration
47+
- phpstan.neon Static analysis configuration
48+
49+
---
50+
51+
## 💡 Code Design Principles
52+
53+
1. **Modularity:**
54+
The core remains framework-like — decoupled from frontend or API layers.
55+
56+
2. **Dependency Injection:**
57+
Use Symfony’s service container; avoid static/global dependencies.
58+
59+
3. **Strict Typing:**
60+
Always use `declare(strict_types=1);` and explicit type declarations.
61+
62+
4. **Doctrine Entities:**
63+
- Keep entities simple (no business logic).
64+
- Mirror schema changes in `resources/Database/Schema.sql`.
65+
- Maintain backward compatibility with phpList 3.
66+
67+
5. **Symfony Best Practices:**
68+
Follow Symfony structure and naming conventions. Use annotations or attributes for routing.
69+
70+
6. **Error Handling & Logging:**
71+
- Prefer structured logging via Graylog.
72+
- Catch and handle exceptions at service or command boundaries.
73+
74+
7. **Async Email:**
75+
- Uses Symfony Messenger.
76+
- Handlers must be idempotent and retry-safe.
77+
- Avoid blocking or synchronous email sending.
78+
79+
---
80+
81+
## 🧪 Testing Guidelines
82+
83+
- **Framework:** PHPUnit
84+
- **Database:** SQLite or mocks for unit tests; MySQL for integration tests.
85+
- **Coverage target:** ≥85% for core logic.
86+
- **Naming:** Mirror source structure (e.g., `Mailer.php``MailerTest.php`).
87+
88+
89+
## 🧱 Code Style
90+
91+
- Follow PSR-12 and Symfony coding conventions.
92+
- Match the current codebase’s formatting and spacing.
93+
- Use meaningful, consistent naming.
94+
- Apply a single responsibility per class.
95+
96+
97+
## 🔄 Pull Request Review Guidelines
98+
### 🔐 Security Review Notes
99+
100+
- Do not log sensitive data (passwords, tokens, SMTP credentials).
101+
- Sanitize all user and external inputs.
102+
- Always use parameterized Doctrine queries.
103+
- Async jobs must be retry-safe and idempotent.

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
1-
### Summary
2-
3-
Provide a general description of the code changes in your pull request …
4-
were there any bugs you had fixed? If so, mention them. If these bugs have open
5-
GitHub issues, be sure to tag them here as well, to keep the conversation
6-
linked together.
7-
1+
"@coderabbitai summary"
82

93
### Unit test
104

@@ -17,7 +11,7 @@ You can run the existing unit tests using this command:
1711

1812
### Code style
1913

20-
Have you checked that you code is well-documented and follows the PSR-2 coding
14+
Have you checked that your code is well-documented and follows the PSR-2 coding
2115
style?
2216

2317
You can check for this using this command:
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpList\Core\Domain\Identity\Command;
6+
7+
use Doctrine\ORM\EntityManagerInterface;
8+
use PhpList\Core\Domain\Identity\Model\Dto\CreateAdministratorDto;
9+
use PhpList\Core\Domain\Identity\Model\PrivilegeFlag;
10+
use PhpList\Core\Domain\Identity\Repository\AdministratorRepository;
11+
use PhpList\Core\Domain\Identity\Service\AdministratorManager;
12+
use Symfony\Component\Console\Attribute\AsCommand;
13+
use Symfony\Component\Console\Command\Command;
14+
use Symfony\Component\Console\Helper\QuestionHelper;
15+
use Symfony\Component\Console\Input\InputInterface;
16+
use Symfony\Component\Console\Output\OutputInterface;
17+
use Symfony\Component\Console\Question\Question;
18+
19+
#[AsCommand(
20+
name: 'phplist:defaults:import',
21+
description: 'Imports default values into the database (e.g., default admin with all privileges).'
22+
)]
23+
class ImportDefaultsCommand extends Command
24+
{
25+
private const DEFAULT_LOGIN = 'admin';
26+
private const DEFAULT_EMAIL = '[email protected]';
27+
28+
public function __construct(
29+
private readonly AdministratorRepository $administratorRepository,
30+
private readonly AdministratorManager $administratorManager,
31+
private readonly EntityManagerInterface $entityManager,
32+
) {
33+
parent::__construct();
34+
}
35+
36+
protected function execute(InputInterface $input, OutputInterface $output): int
37+
{
38+
$login = self::DEFAULT_LOGIN;
39+
$email = self::DEFAULT_EMAIL;
40+
$envPassword = getenv('PHPLIST_ADMIN_PASSWORD');
41+
$envPassword = is_string($envPassword) && trim($envPassword) !== '' ? $envPassword : null;
42+
43+
$allPrivileges = $this->allPrivilegesGranted();
44+
45+
$existing = $this->administratorRepository->findOneBy(['loginName' => $login]);
46+
if ($existing === null) {
47+
// If creating the default admin, require a password. Prefer env var, else prompt for input.
48+
$password = $envPassword;
49+
if ($password === null) {
50+
/** @var QuestionHelper $helper */
51+
$helper = $this->getHelper('question');
52+
$question = new Question('Enter password for default admin (login "admin"): ');
53+
$question->setHidden(true);
54+
$question->setHiddenFallback(false);
55+
$password = (string) $helper->ask($input, $output, $question);
56+
if (trim($password) === '') {
57+
$output->writeln('<error>Password must not be empty.</error>');
58+
return Command::FAILURE;
59+
}
60+
}
61+
62+
$dto = new CreateAdministratorDto(
63+
loginName: $login,
64+
password: $password,
65+
email: $email,
66+
isSuperUser: true,
67+
privileges: $allPrivileges,
68+
);
69+
$admin = $this->administratorManager->createAdministrator($dto);
70+
$this->entityManager->flush();
71+
72+
$output->writeln(sprintf(
73+
'Default admin created: login="%s", email="%s", superuser=yes, privileges=all',
74+
$admin->getLoginName(),
75+
$admin->getEmail()
76+
));
77+
} else {
78+
$output->writeln(sprintf(
79+
'Default admin already exists: login="%s", email="%s"',
80+
$existing->getLoginName(),
81+
$existing->getEmail(),
82+
));
83+
}
84+
85+
return Command::SUCCESS;
86+
}
87+
88+
/**
89+
* @return array<string,bool>
90+
* @SuppressWarnings(PHPMD.StaticAccess)
91+
*/
92+
private function allPrivilegesGranted(): array
93+
{
94+
$all = [];
95+
foreach (PrivilegeFlag::cases() as $flag) {
96+
$all[$flag->value] = true;
97+
}
98+
return $all;
99+
}
100+
}

src/Migrations/Version20251103SeedInitialAdmin.php

Lines changed: 0 additions & 49 deletions
This file was deleted.

0 commit comments

Comments
 (0)