Skip to content

Commit 95ed840

Browse files
HTTP mailbox
1 parent 4522401 commit 95ed840

19 files changed

+778
-2
lines changed

bin/app.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Symfony\Component\DependencyInjection\ContainerBuilder;
99
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
1010
use Symfony\Component\Filesystem\Path;
11+
use Symfony\Component\Routing\DependencyInjection\RoutingResolverPass;
1112

1213
return function (string $projectDir, bool $debug = true): SmtpDevCachedContainer {
1314
$file = "{$projectDir}/var/cache/SmtpDevCachedContainer.php";
@@ -19,6 +20,9 @@
1920

2021
$loader = new DependencyInjection\Loader\PhpFileLoader($containerBuilder, $locator);
2122
$loader->load('services/services.php');
23+
$loader->load('services/routing.php');
24+
25+
$containerBuilder->addCompilerPass(new RoutingResolverPass());
2226

2327
$containerBuilder->compile();
2428

bin/smtp-dev-client

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/usr/bin/env php
2+
<?php
3+
4+
use Camelot\SmtpDevServer\Command\HttpServerCommand;
5+
use Symfony\Component\Console\Input\ArgvInput;
6+
use Symfony\Component\Console\Output\ConsoleOutput;
7+
use Symfony\Component\DependencyInjection\Container;
8+
9+
$run = function (): void {
10+
if (is_file(dirname(__DIR__) . '/vendor/autoload.php')) {
11+
$baseDir = dirname(__DIR__);
12+
require_once $baseDir . '/vendor/autoload.php';
13+
} elseif (is_file(dirname(__DIR__, 3) . '/autoload.php')) {
14+
$baseDir = dirname(__DIR__, 3);
15+
require_once $baseDir . '/autoload.php';
16+
} else {
17+
throw new LogicException('Composer autoload is missing. Try running "composer install".');
18+
}
19+
20+
$builder = require_once __DIR__ . '/app.php';
21+
/** @var Container $container */
22+
$container = $builder($baseDir);
23+
$command = $container->get(HttpServerCommand::class);
24+
25+
$command->run(new ArgvInput(), new ConsoleOutput());
26+
};
27+
28+
$run();
29+

composer.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,16 @@
1414
"require": {
1515
"php": ">=8.1",
1616
"monolog/monolog": "^3.0",
17+
"php-mime-mail-parser/php-mime-mail-parser": "^7.1",
1718
"psr/log": "^2.0 || ^3.0",
19+
"symfony/config": "^6.0",
1820
"symfony/console": "^6.0",
21+
"symfony/dependency-injection": "^6.0",
1922
"symfony/event-dispatcher": "^6.0",
20-
"php-mime-mail-parser/php-mime-mail-parser": "^7.1"
23+
"symfony/http-foundation": "^6.0",
24+
"symfony/http-kernel": "^6.0",
25+
"symfony/routing": "^6.0",
26+
"twig/twig": "^3.4"
2127
},
2228
"require-dev": {
2329
"camelot/coding-style": "^3.0",

config/routes.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Camelot\SmtpDevServer\Controller\AssetController;
6+
use Camelot\SmtpDevServer\Controller\MailboxController;
7+
use Symfony\Component\HttpFoundation\Request;
8+
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
9+
10+
return function (RoutingConfigurator $routes): void {
11+
$routes->add('mailbox', '/')
12+
->controller(MailboxController::class)
13+
->methods([Request::METHOD_GET])
14+
;
15+
$routes->add('asset', '/asset/{asset}')
16+
->controller(AssetController::class)
17+
->methods([Request::METHOD_GET])
18+
;
19+
};

config/services/routing.php

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Camelot\SmtpDevServer\Controller;
6+
use Symfony\Component\Config\FileLocator;
7+
use Symfony\Component\Config\Loader\DelegatingLoader;
8+
use Symfony\Component\Config\Loader\LoaderResolver;
9+
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
10+
use Symfony\Component\HttpFoundation\RequestStack;
11+
use Symfony\Component\HttpKernel\Controller\ArgumentResolver;
12+
use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface;
13+
use Symfony\Component\HttpKernel\Controller\ContainerControllerResolver;
14+
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
15+
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory;
16+
use Symfony\Component\Routing;
17+
use Symfony\Component\Routing\Generator\CompiledUrlGenerator;
18+
use Symfony\Component\Routing\Generator\Dumper\CompiledUrlGeneratorDumper;
19+
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
20+
use Symfony\Component\Routing\Matcher\CompiledUrlMatcher;
21+
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherDumper;
22+
use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
23+
use Symfony\Component\Routing\RequestContext;
24+
use Symfony\Component\Routing\RequestContextAwareInterface;
25+
use Symfony\Component\Routing\Router;
26+
use Symfony\Component\Routing\RouterInterface;
27+
use function Symfony\Component\DependencyInjection\Loader\Configurator\param;
28+
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
29+
30+
return static function (ContainerConfigurator $configurator): void {
31+
$parameters = $configurator->parameters();
32+
33+
$parameters->set('router.resource', 'config/routes.php');
34+
$parameters->set('request_listener.http_port', 80);
35+
$parameters->set('request_listener.https_port', 443);
36+
$parameters->set('router.request_context.base_url', '');
37+
$parameters->set('router.request_context.host', 'localhost');
38+
$parameters->set('router.request_context.scheme', 'https');
39+
40+
$services = $configurator->services();
41+
$services->defaults()
42+
->autoconfigure()
43+
->autowire()
44+
->bind('$projectDir', '%kernel.project_dir%')
45+
;
46+
47+
$services->set('controller_resolver', ContainerControllerResolver::class)
48+
->args([
49+
service('service_container'),
50+
service('logger'),
51+
])
52+
->tag('monolog.logger', ['channel' => 'request'])
53+
->alias(ControllerResolverInterface::class, 'controller_resolver')
54+
;
55+
56+
$services->set('argument_metadata_factory', ArgumentMetadataFactory::class);
57+
58+
$services->set('argument_resolver', ArgumentResolver::class)
59+
->alias(ArgumentResolverInterface::class, 'argument_resolver')
60+
;
61+
62+
$services->set('request_stack', RequestStack::class)
63+
->alias(RequestStack::class, 'request_stack')
64+
->public()
65+
;
66+
67+
$services->set('routing.resolver', LoaderResolver::class);
68+
69+
$services->set('file_locator', FileLocator::class)
70+
->arg('$paths', '%kernel.project_dir%')
71+
->alias(FileLocator::class, 'file_locator')
72+
;
73+
74+
$services->set('routing.loader.php', Routing\Loader\PhpFileLoader::class)
75+
->args([
76+
service('file_locator'),
77+
'%kernel.environment%',
78+
])
79+
->tag('routing.loader')
80+
;
81+
$services->set('routing.loader', DelegatingLoader::class)
82+
->public()
83+
->args([
84+
service('routing.resolver'),
85+
[], // Default options
86+
[], // Default requirements
87+
])
88+
;
89+
$services->set('router.default', Router::class)
90+
->arg('$loader', service('routing.loader'))
91+
->arg('$resource', param('router.resource'))
92+
->arg('$options', [
93+
'cache_dir' => param('kernel.cache_dir'),
94+
'debug' => param('kernel.debug'),
95+
'generator_class' => CompiledUrlGenerator::class,
96+
'generator_dumper_class' => CompiledUrlGeneratorDumper::class,
97+
'matcher_class' => CompiledUrlMatcher::class,
98+
'matcher_dumper_class' => CompiledUrlMatcherDumper::class,
99+
])
100+
->arg('$context', service('router.request_context'))
101+
->arg('$logger', service('logger'))
102+
->arg('$defaultLocale', param('kernel.default_locale'))
103+
->call('setConfigCacheFactory', [
104+
service('config_cache_factory'),
105+
])
106+
->tag('monolog.logger', ['channel' => 'router'])
107+
->alias('router', 'router.default')->public()
108+
->alias(RouterInterface::class, 'router')
109+
->alias(UrlGeneratorInterface::class, 'router')
110+
->alias(UrlMatcherInterface::class, 'router')
111+
->alias(RequestContextAwareInterface::class, 'router')
112+
;
113+
114+
$services->set('router.request_context', RequestContext::class)
115+
->factory([RequestContext::class, 'fromUri'])
116+
->args([
117+
param('router.request_context.base_url'),
118+
param('router.request_context.host'),
119+
param('router.request_context.scheme'),
120+
param('request_listener.http_port'),
121+
param('request_listener.https_port'),
122+
])
123+
->call('setParameter', [
124+
'_functions',
125+
service('router.expression_language_provider')->ignoreOnInvalid(),
126+
])
127+
->alias(RequestContext::class, 'router.request_context')
128+
;
129+
130+
$services->set(Controller\MailboxController::class)
131+
->tag('controller.service_arguments')
132+
->public()
133+
;
134+
135+
$services->set(Controller\AssetController::class)
136+
->tag('controller.service_arguments')
137+
->public()
138+
;
139+
};

config/services/services.php

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Camelot\SmtpDevServer\Command;
66
use Camelot\SmtpDevServer\Event;
7+
use Camelot\SmtpDevServer\Mailbox;
78
use Camelot\SmtpDevServer\Server;
89
use Camelot\SmtpDevServer\Storage;
910
use Monolog\Handler\StreamHandler;
@@ -17,7 +18,11 @@
1718
use Symfony\Component\EventDispatcher\EventDispatcher;
1819
use Symfony\Component\EventDispatcher\EventDispatcherInterface as EventDispatcherInterfaceComponentAlias;
1920
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
21+
use Twig\Cache\FilesystemCache;
22+
use Twig\Environment;
23+
use Twig\Loader\FilesystemLoader;
2024

25+
use function Symfony\Component\DependencyInjection\Loader\Configurator\param;
2126
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
2227
use function Symfony\Component\DependencyInjection\Loader\Configurator\tagged_iterator;
2328

@@ -65,21 +70,53 @@
6570
$services->set('monolog.handler.stream.smtp', StreamHandler::class)
6671
->args(['%kernel.project_dir%/var/log/smtp.log', 100]) // Level::Debug
6772
;
73+
$services->set('monolog.handler.stream.http', StreamHandler::class)
74+
->args(['%kernel.project_dir%/var/log/http.log', 100]) // Level::Debug
75+
;
6876

6977
$services->set('monolog.logger.smtp', Logger::class)
7078
->tag('monolog.logger', ['channel' => 'smtp'])
7179
->args(['smtp'])
7280
;
81+
$services->set('monolog.logger.http', Logger::class)
82+
->tag('monolog.logger', ['channel' => 'smtp'])
83+
->args(['http'])
84+
;
7385

7486
$services->set('event_dispatcher', EventDispatcher::class)
7587
->call('addSubscriber', [service(Event\SmtpEventListener::class)])
88+
->call('addSubscriber', [service(Event\HttpEventListener::class)])
7689
->public()
7790
->tag('container.hot_path')
7891
->tag('event_dispatcher.dispatcher', ['name' => 'event_dispatcher'])
7992
->alias(EventDispatcherInterfaceComponentAlias::class, 'event_dispatcher')
8093
->alias(EventDispatcherInterface::class, 'event_dispatcher')
8194
;
8295
$services->set(Event\SmtpEventListener::class);
96+
$services->set(Event\HttpEventListener::class);
97+
98+
$services->set('twig', Environment::class)
99+
->args([
100+
service('twig.loader'),
101+
[
102+
'debug' => true,
103+
'charset' => 'UTF-8',
104+
'strict_variables' => true,
105+
'autoescape' => 'html',
106+
'cache' => false,
107+
'auto_reload' => null,
108+
'optimizations' => -1,
109+
],
110+
])
111+
->tag('container.preload', ['class' => FilesystemCache::class])
112+
->alias(Environment::class, 'twig')
113+
;
114+
115+
$services->set('twig.loader.native_filesystem', FilesystemLoader::class)
116+
->args([['templates'], param('kernel.project_dir')])
117+
->tag('twig.loader')
118+
->alias('twig.loader', 'twig.loader.native_filesystem')
119+
;
83120

84121
$services->set(Storage\NullStorage::class);
85122
$services->set(Storage\MemoryStorage::class);
@@ -90,9 +127,24 @@
90127
->args([service(EventDispatcherInterface::class), service('monolog.logger.smtp')])
91128
;
92129

130+
$services->set('server.http', Server::class)
131+
->args([service(EventDispatcherInterface::class), service('monolog.logger.smtp')])
132+
;
133+
134+
$services->set(Mailbox::class)
135+
->arg('$spoolDir', '%kernel.project_dir%/var/spool')
136+
->public()
137+
;
138+
93139
$services->set(Command\SmtpServerCommand::class)
94140
->args([service('server.smtp'), service('monolog.logger.smtp')])
95141
->tag('console.command')
96142
->public()
97143
;
144+
145+
$services->set(Command\HttpServerCommand::class)
146+
->args([service('server.http'), service('monolog.logger.http')])
147+
->tag('console.command')
148+
->public()
149+
;
98150
};

public/asset/bulma.min.css

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/asset/mailbox.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.card-content .content {
2+
overflow-wrap: break-word;
3+
}

public/asset/mailbox.svg

Lines changed: 24 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)