Compare commits
13 Commits
c8cf2892c9
...
f4b08e4c73
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4b08e4c73 | ||
|
|
db5c57ea5c | ||
|
|
0a56c741e8 | ||
|
|
668a67c094 | ||
|
|
acf3229f3f | ||
|
|
5367449d80 | ||
|
|
68407b944e | ||
|
|
c12d155e23 | ||
|
|
3f08f6189e | ||
|
|
b8fc55bcc2 | ||
|
|
6cfc8e7533 | ||
|
|
2e26cce91f | ||
|
|
8e8ca4d5e7 |
90
.github/workflows/deploy.yaml
vendored
Normal file
90
.github/workflows/deploy.yaml
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
name: deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
qa:
|
||||
name: QA gates
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
COMPOSE_PROJECT_NAME: breadcrumb-ci
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Create .env for CI
|
||||
run: cp .env.example .env
|
||||
|
||||
- name: Start project services
|
||||
run: |
|
||||
docker compose up -d --force-recreate php db memcached
|
||||
sleep 20
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: |
|
||||
docker compose exec -T php bash -c "cd /var/www && composer install --no-interaction --prefer-dist --optimize-autoloader"
|
||||
|
||||
- name: Run DB migrations
|
||||
run: docker compose exec -T php php bin/console db:migrate
|
||||
|
||||
- name: Run required QA gates
|
||||
run: bin/qa-required.sh
|
||||
|
||||
build-and-deploy:
|
||||
name: Build & deploy
|
||||
runs-on: ubuntu-latest
|
||||
needs: qa
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build production image
|
||||
run: |
|
||||
docker build \
|
||||
--target prod \
|
||||
-f docker/php/Dockerfile \
|
||||
-t breadcrumb-the-shire:latest \
|
||||
.
|
||||
|
||||
- name: Export image to tar.gz
|
||||
run: docker save breadcrumb-the-shire:latest | gzip > breadcrumb-the-shire.tar.gz
|
||||
|
||||
- name: Copy image to server
|
||||
uses: appleboy/scp-action@v0.1.7
|
||||
with:
|
||||
host: ${{ secrets.DEPLOY_HOST }}
|
||||
username: ${{ secrets.DEPLOY_USER }}
|
||||
key: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||
source: breadcrumb-the-shire.tar.gz
|
||||
target: /root/dockerfiles-breadcrumb-infra/projects/
|
||||
|
||||
- name: Copy updated deploy files to server
|
||||
uses: appleboy/scp-action@v0.1.7
|
||||
with:
|
||||
host: ${{ secrets.DEPLOY_HOST }}
|
||||
username: ${{ secrets.DEPLOY_USER }}
|
||||
key: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||
source: "deploy/docker-compose.yml,deploy/nginx/"
|
||||
target: /root/dockerfiles-breadcrumb-infra/projects/
|
||||
strip_components: 1
|
||||
|
||||
- name: Deploy on server
|
||||
uses: appleboy/ssh-action@v1.0.3
|
||||
with:
|
||||
host: ${{ secrets.DEPLOY_HOST }}
|
||||
username: ${{ secrets.DEPLOY_USER }}
|
||||
key: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||
script: |
|
||||
set -e
|
||||
cd /root/dockerfiles-breadcrumb-infra/projects
|
||||
|
||||
docker load < breadcrumb-the-shire.tar.gz
|
||||
rm breadcrumb-the-shire.tar.gz
|
||||
|
||||
docker compose up -d --remove-orphans
|
||||
sleep 5
|
||||
|
||||
docker compose exec -T php php bin/console module:sync
|
||||
39
.github/workflows/qa-required.yaml
vendored
Normal file
39
.github/workflows/qa-required.yaml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: qa-required
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
qa-required:
|
||||
name: qa-required
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
COMPOSE_PROJECT_NAME: breadcrumb-ci
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Create .env for CI
|
||||
run: cp .env.example .env
|
||||
|
||||
- name: Start project services
|
||||
run: |
|
||||
docker compose up -d --force-recreate php db memcached
|
||||
sleep 20
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: |
|
||||
docker compose exec -T php bash -c "cd /var/www && composer install --no-interaction --prefer-dist --optimize-autoloader"
|
||||
|
||||
- name: Run DB migrations
|
||||
run: docker compose exec -T php php bin/console db:migrate
|
||||
|
||||
- name: Install ripgrep
|
||||
run: sudo apt-get install -y ripgrep
|
||||
|
||||
- name: Run required QA gates
|
||||
run: bin/qa-required.sh
|
||||
@@ -59,7 +59,7 @@ class AuthServicesFactory
|
||||
$this->authRepositoryFactory->createPasswordResetRepository(),
|
||||
$this->userServicesFactory->createUserPasswordService(),
|
||||
$this->createRememberMeService(),
|
||||
$this->mailServicesFactory->createMailService()
|
||||
$this->mailServicesFactory->createMailService(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ class AuthServicesFactory
|
||||
$this->userServicesFactory->createUserReadRepository(),
|
||||
$this->userServicesFactory->createUserWriteRepository(),
|
||||
$this->authRepositoryFactory->createEmailVerificationRepository(),
|
||||
$this->mailServicesFactory->createMailService()
|
||||
$this->mailServicesFactory->createMailService(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,10 +19,25 @@ class EmailVerificationService
|
||||
private readonly UserReadRepositoryInterface $userReadRepository,
|
||||
private readonly UserWriteRepositoryInterface $userWriteRepository,
|
||||
private readonly EmailVerificationRepositoryInterface $emailVerificationRepository,
|
||||
private readonly MailService $mailService
|
||||
private readonly MailService $mailService,
|
||||
private readonly string $appTitle = '',
|
||||
private readonly string $appUrl = '',
|
||||
) {
|
||||
}
|
||||
|
||||
private function resolveAppTitle(): string
|
||||
{
|
||||
return $this->appTitle !== '' ? $this->appTitle : appTitle();
|
||||
}
|
||||
|
||||
private function resolveAppUrl(string $path = ''): string
|
||||
{
|
||||
if ($this->appUrl !== '') {
|
||||
return rtrim($this->appUrl, '/') . ($path !== '' ? '/' . ltrim($path, '/') : '');
|
||||
}
|
||||
return appUrl($path);
|
||||
}
|
||||
|
||||
public function sendVerification(int $userId, ?string $locale = null): array
|
||||
{
|
||||
$user = $this->userReadRepository->find($userId);
|
||||
@@ -55,17 +70,17 @@ class EmailVerificationService
|
||||
}
|
||||
$greeting .= ',';
|
||||
$verifyPath = Request::withLocale('verify-email', $locale);
|
||||
$verifyUrl = appUrl($verifyPath);
|
||||
$verifyUrl = $this->resolveAppUrl($verifyPath);
|
||||
$previousLocale = I18n::$locale ?? null;
|
||||
I18n::$locale = $locale;
|
||||
$subject = t('Email verification code');
|
||||
I18n::$locale = $previousLocale;
|
||||
|
||||
$vars = [
|
||||
'app_name' => appTitle(),
|
||||
'app_logo_url' => appLogoUrlAbsolute(128),
|
||||
'imprint_url' => appUrl(Request::withLocale('imprint', $locale)),
|
||||
'privacy_url' => appUrl(Request::withLocale('privacy', $locale)),
|
||||
'app_name' => $this->resolveAppTitle(),
|
||||
'app_logo_url' => $this->resolveAppUrl(appLogoUrl(128)),
|
||||
'imprint_url' => $this->resolveAppUrl(Request::withLocale('imprint', $locale)),
|
||||
'privacy_url' => $this->resolveAppUrl(Request::withLocale('privacy', $locale)),
|
||||
'code' => $code,
|
||||
'expires_minutes' => self::EXPIRY_MINUTES,
|
||||
'verify_url' => $verifyUrl,
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
namespace MintyPHP\Service\Auth;
|
||||
|
||||
use MintyPHP\Curl;
|
||||
|
||||
class OidcHttpGateway
|
||||
{
|
||||
/**
|
||||
@@ -49,7 +47,7 @@ class OidcHttpGateway
|
||||
$body = @file_get_contents($url, false, $ctx);
|
||||
$status = 0;
|
||||
$responseHeaders = [];
|
||||
if (isset($http_response_header) && is_array($http_response_header)) {
|
||||
if ($http_response_header !== []) {
|
||||
foreach ($http_response_header as $i => $line) {
|
||||
if ($i === 0 && preg_match('/HTTP\/[\d.]+ (\d+)/', $line, $m)) {
|
||||
$status = (int) $m[1];
|
||||
|
||||
@@ -20,10 +20,25 @@ class PasswordResetService
|
||||
private readonly PasswordResetRepositoryInterface $passwordResetRepository,
|
||||
private readonly UserPasswordService $userPasswordService,
|
||||
private readonly RememberMeService $rememberMeService,
|
||||
private readonly MailService $mailService
|
||||
private readonly MailService $mailService,
|
||||
private readonly string $appTitle = '',
|
||||
private readonly string $appUrl = '',
|
||||
) {
|
||||
}
|
||||
|
||||
private function resolveAppTitle(): string
|
||||
{
|
||||
return $this->appTitle !== '' ? $this->appTitle : appTitle();
|
||||
}
|
||||
|
||||
private function resolveAppUrl(string $path = ''): string
|
||||
{
|
||||
if ($this->appUrl !== '') {
|
||||
return rtrim($this->appUrl, '/') . ($path !== '' ? '/' . ltrim($path, '/') : '');
|
||||
}
|
||||
return appUrl($path);
|
||||
}
|
||||
|
||||
public function requestReset(string $email, ?string $locale = null): array
|
||||
{
|
||||
$email = trim($email);
|
||||
@@ -58,17 +73,17 @@ class PasswordResetService
|
||||
}
|
||||
$greeting .= ',';
|
||||
$verifyPath = Request::withLocale('password/verify', $locale);
|
||||
$verifyUrl = appUrl($verifyPath);
|
||||
$verifyUrl = $this->resolveAppUrl($verifyPath);
|
||||
$previousLocale = I18n::$locale ?? null;
|
||||
I18n::$locale = $locale;
|
||||
$subject = t('Password reset code');
|
||||
I18n::$locale = $previousLocale;
|
||||
|
||||
$vars = [
|
||||
'app_name' => appTitle(),
|
||||
'app_logo_url' => appLogoUrlAbsolute(128),
|
||||
'imprint_url' => appUrl(Request::withLocale('imprint', $locale)),
|
||||
'privacy_url' => appUrl(Request::withLocale('privacy', $locale)),
|
||||
'app_name' => $this->resolveAppTitle(),
|
||||
'app_logo_url' => $this->resolveAppUrl(appLogoUrl(128)),
|
||||
'imprint_url' => $this->resolveAppUrl(Request::withLocale('imprint', $locale)),
|
||||
'privacy_url' => $this->resolveAppUrl(Request::withLocale('privacy', $locale)),
|
||||
'code' => $code,
|
||||
'expires_minutes' => self::EXPIRY_MINUTES,
|
||||
'verify_url' => $verifyUrl,
|
||||
|
||||
@@ -145,7 +145,9 @@ final class HelpdeskContainerRegistrar implements ContainerRegistrar
|
||||
));
|
||||
|
||||
$container->set(HandoverNotificationService::class, static fn (AppContainer $c): HandoverNotificationService => new HandoverNotificationService(
|
||||
$c->get(MailService::class)
|
||||
$c->get(MailService::class),
|
||||
appTitle(),
|
||||
appUrl(),
|
||||
));
|
||||
|
||||
$container->set(HandoverService::class, static fn (AppContainer $c): HandoverService => new HandoverService(
|
||||
|
||||
@@ -8,9 +8,24 @@ class HandoverNotificationService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly MailService $mailService,
|
||||
private readonly string $appTitle = '',
|
||||
private readonly string $appUrl = '',
|
||||
) {
|
||||
}
|
||||
|
||||
private function resolveAppTitle(): string
|
||||
{
|
||||
return $this->appTitle !== '' ? $this->appTitle : appTitle();
|
||||
}
|
||||
|
||||
private function resolveAppUrl(string $path = ''): string
|
||||
{
|
||||
if ($this->appUrl !== '') {
|
||||
return rtrim($this->appUrl, '/') . ($path !== '' ? '/' . ltrim($path, '/') : '');
|
||||
}
|
||||
return appUrl($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the assignee that a handover was assigned to them.
|
||||
*
|
||||
@@ -29,7 +44,7 @@ class HandoverNotificationService
|
||||
$domainUrl = trim((string) ($handover['domain_url'] ?? ''));
|
||||
$dueDate = trim((string) ($handover['due_date'] ?? ''));
|
||||
$handoverId = (int) ($handover['id'] ?? 0);
|
||||
$handoverUrl = appUrl('helpdesk/handovers/edit/' . $handoverId);
|
||||
$handoverUrl = $this->resolveAppUrl('helpdesk/handovers/edit/' . $handoverId);
|
||||
|
||||
$vars = [
|
||||
'assignee_name' => $assigneeName,
|
||||
@@ -38,10 +53,10 @@ class HandoverNotificationService
|
||||
'domain_url' => $domainUrl !== '' ? $domainUrl : '—',
|
||||
'due_date' => $dueDate !== '' ? $dueDate : '—',
|
||||
'handover_url' => $handoverUrl,
|
||||
'app_name' => appTitle(),
|
||||
'app_name' => $this->resolveAppTitle(),
|
||||
];
|
||||
|
||||
$subject = sprintf('[%s] Ihnen wurde eine Übergabe zugewiesen', appTitle());
|
||||
$subject = sprintf('[%s] Ihnen wurde eine Übergabe zugewiesen', $this->resolveAppTitle());
|
||||
|
||||
$this->mailService->sendTemplate('handover_assigned', $vars, $toEmail, $subject);
|
||||
}
|
||||
@@ -69,7 +84,7 @@ class HandoverNotificationService
|
||||
$debitorName = trim((string) ($handover['debitor_name'] ?? ''));
|
||||
$domainUrl = trim((string) ($handover['domain_url'] ?? ''));
|
||||
$handoverId = (int) ($handover['id'] ?? 0);
|
||||
$handoverUrl = appUrl('helpdesk/handovers/edit/' . $handoverId);
|
||||
$handoverUrl = $this->resolveAppUrl('helpdesk/handovers/edit/' . $handoverId);
|
||||
|
||||
$vars = [
|
||||
'manager_name' => $managerName,
|
||||
@@ -77,10 +92,10 @@ class HandoverNotificationService
|
||||
'debitor_name' => $debitorName !== '' ? $debitorName : '—',
|
||||
'domain_url' => $domainUrl !== '' ? $domainUrl : '—',
|
||||
'handover_url' => $handoverUrl,
|
||||
'app_name' => appTitle(),
|
||||
'app_name' => $this->resolveAppTitle(),
|
||||
];
|
||||
|
||||
$subject = sprintf('[%s] Übergabe wurde zur Prüfung eingereicht', appTitle());
|
||||
$subject = sprintf('[%s] Übergabe wurde zur Prüfung eingereicht', $this->resolveAppTitle());
|
||||
|
||||
$this->mailService->sendTemplate('handover_review_requested', $vars, $toEmail, $subject);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,10 @@ return gridFilterSchema([
|
||||
'search' => true,
|
||||
'query' => ['type' => 'string'],
|
||||
],
|
||||
[
|
||||
'key' => 'view',
|
||||
'type' => 'hidden',
|
||||
],
|
||||
[
|
||||
'key' => 'status',
|
||||
'type' => 'select',
|
||||
|
||||
@@ -11,7 +11,9 @@ class HandoverNotificationServiceTest extends TestCase
|
||||
private function createNotificationService(?MailService $mailService = null): HandoverNotificationService
|
||||
{
|
||||
return new HandoverNotificationService(
|
||||
$mailService ?? $this->createMock(MailService::class)
|
||||
$mailService ?? $this->createMock(MailService::class),
|
||||
'TestApp',
|
||||
'http://localhost',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -309,7 +309,9 @@ class EmailVerificationServiceTest extends TestCase
|
||||
$userReadRepository,
|
||||
$userWriteRepository,
|
||||
$emailVerificationRepository,
|
||||
$mailService
|
||||
$mailService,
|
||||
'TestApp',
|
||||
'http://localhost',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,7 +356,9 @@ class PasswordResetServiceTest extends TestCase
|
||||
$passwordResetRepository,
|
||||
$userPasswordService,
|
||||
$rememberMeService,
|
||||
$mailService
|
||||
$mailService,
|
||||
'TestApp',
|
||||
'http://localhost',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user