mirror of
https://ghproxy.net/https://github.com/aspirepress/AspireCloud.git
synced 2025-10-04 21:34:02 +08:00
Initial commit.
This commit is contained in:
commit
63686360bc
49 changed files with 8673 additions and 0 deletions
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
/.phpcs-cache
|
||||
/.phpunit.result.cache
|
||||
/clover.xml
|
||||
/coveralls-upload.json
|
||||
/phpunit.xml
|
||||
/vendor/
|
||||
docker-compose.override.yml
|
||||
/assets/output/*
|
||||
/node_modules/
|
||||
/logs/error.log
|
27
Makefile
Normal file
27
Makefile
Normal file
|
@ -0,0 +1,27 @@
|
|||
.PHONY: *
|
||||
|
||||
OPTS=
|
||||
|
||||
unit:
|
||||
docker compose run --rm webapp bash -c "vendor/bin/phpunit --testsuite=unit ${OPTS}"
|
||||
|
||||
functional:
|
||||
docker compose run --rm webapp bash -c "vendor/bin/phpunit --testsuite=functional ${OPTS}"
|
||||
|
||||
test:
|
||||
docker compose run --rm webapp bash -c "vendor/bin/phpunit ${OPTS}"
|
||||
|
||||
acceptance:
|
||||
docker compose run --rm webapp bash -c "vendor/bin/behat -vvv ${OPTS}"
|
||||
|
||||
psalm:
|
||||
docker compose run --rm webapp bash -c "vendor/bin/psalm --show-info=false ${OPTS}"
|
||||
|
||||
css:
|
||||
docker compose run --rm node bash -c "npx tailwindcss -i ./assets/source/style.css -o ./assets/output/style.css"
|
||||
|
||||
css-watch:
|
||||
docker compose run --rm node bash -c "npx tailwindcss -i ./assets/source/style.css -o ./assets/output/style.css --watch"
|
||||
|
||||
install-node:
|
||||
docker compose run --rm node bash -c "npm install"
|
3
assets/source/style.css
Normal file
3
assets/source/style.css
Normal file
|
@ -0,0 +1,3 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
14
behat.yml
Normal file
14
behat.yml
Normal file
|
@ -0,0 +1,14 @@
|
|||
default:
|
||||
autoload:
|
||||
'': '%paths.base%/tests/acceptance/src'
|
||||
suites:
|
||||
default:
|
||||
autowire: true
|
||||
services: "@psr_container"
|
||||
paths:
|
||||
- '%paths.base%/tests/acceptance/features'
|
||||
contexts:
|
||||
- AppTest\Acceptance\SampleContext
|
||||
extensions:
|
||||
Roave\BehatPsrContainer\PsrContainerExtension:
|
||||
container: '%paths.base%/tests/acceptance/container.php'
|
39
bin/clear-config-cache.php
Normal file
39
bin/clear-config-cache.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
chdir(__DIR__ . '/../');
|
||||
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
$config = include 'config/config.php';
|
||||
|
||||
if (! isset($config['config_cache_path'])) {
|
||||
echo "No configuration cache path found" . PHP_EOL;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (! file_exists($config['config_cache_path'])) {
|
||||
printf(
|
||||
"Configured config cache file '%s' not found%s",
|
||||
$config['config_cache_path'],
|
||||
PHP_EOL
|
||||
);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (false === unlink($config['config_cache_path'])) {
|
||||
printf(
|
||||
"Error removing config cache file '%s'%s",
|
||||
$config['config_cache_path'],
|
||||
PHP_EOL
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf(
|
||||
"Removed configured config cache file '%s'%s",
|
||||
$config['config_cache_path'],
|
||||
PHP_EOL
|
||||
);
|
||||
exit(0);
|
69
composer.json
Normal file
69
composer.json
Normal file
|
@ -0,0 +1,69 @@
|
|||
{
|
||||
"name": "tailwindsllc/project-skeleton",
|
||||
"description": "A project skeleton for Tailwinds projects.",
|
||||
"config": {
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true,
|
||||
"composer/package-versions-deprecated": true,
|
||||
"laminas/laminas-component-installer": true
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": "~8.0.0 || ~8.1.0",
|
||||
"composer/package-versions-deprecated": "^1.10.99",
|
||||
"laminas/laminas-component-installer": "^2.6",
|
||||
"laminas/laminas-config-aggregator": "^1.6",
|
||||
"laminas/laminas-diactoros": "^2.7",
|
||||
"laminas/laminas-servicemanager": "^3.4",
|
||||
"laminas/laminas-stdlib": "^3.6",
|
||||
"mezzio/mezzio": "^3.7",
|
||||
"mezzio/mezzio-fastroute": "^3.0.3",
|
||||
"mezzio/mezzio-helpers": "^5.7",
|
||||
"mezzio/mezzio-platesrenderer": "^2.2",
|
||||
"monolog/monolog": "^3.2",
|
||||
"webmozart/assert": "^1.11"
|
||||
},
|
||||
"require-dev": {
|
||||
"behat/behat": "^3.11",
|
||||
"filp/whoops": "^2.7.1",
|
||||
"laminas/laminas-development-mode": "^3.3.0",
|
||||
"mezzio/mezzio-tooling": "^2.1",
|
||||
"phpunit/phpunit": "^9.5.11",
|
||||
"phpstan/phpstan": "^1.9",
|
||||
"roave/behat-psr11extension": "^2.2",
|
||||
"roave/security-advisories": "dev-master"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"AppTest\\Unit\\": "tests/unit/src",
|
||||
"AppTest\\Functional\\": "tests/functional/src",
|
||||
"AppTest\\Acceptance\\": "tests/acceptance/src"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"post-create-project-cmd": [
|
||||
"@development-enable"
|
||||
],
|
||||
"development-disable": "laminas-development-mode disable",
|
||||
"development-enable": "laminas-development-mode enable",
|
||||
"development-status": "laminas-development-mode status",
|
||||
"mezzio": "laminas --ansi",
|
||||
"check": [
|
||||
"@cs-check",
|
||||
"@test"
|
||||
],
|
||||
"clear-config-cache": "php bin/clear-config-cache.php",
|
||||
"enable-codestandard": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run",
|
||||
"cs-check": "phpcs",
|
||||
"cs-fix": "phpcbf",
|
||||
"serve": "php -S 0.0.0.0:8080 -t public/",
|
||||
"test": "phpunit --colors=always",
|
||||
"test-coverage": "phpunit --colors=always --coverage-clover clover.xml"
|
||||
}
|
||||
}
|
6344
composer.lock
generated
Normal file
6344
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
1
config/.gitignore
vendored
Normal file
1
config/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
development.config.php
|
2
config/autoload/.gitignore
vendored
Normal file
2
config/autoload/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
local.php
|
||||
*.local.php
|
24
config/autoload/application.global.php
Normal file
24
config/autoload/application.global.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
/*
|
||||
* Logging Configuration
|
||||
*/
|
||||
'logging' => [
|
||||
'path' => './logs/',
|
||||
'error' => [
|
||||
'file' => 'error.log',
|
||||
'level' => \Monolog\Level::Debug,
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
* Plates Configuration
|
||||
*/
|
||||
'templates' => [
|
||||
'extension' => 'php',
|
||||
'paths' => [
|
||||
'app' => './templates/app',
|
||||
],
|
||||
],
|
||||
];
|
26
config/autoload/dependencies.global.php
Normal file
26
config/autoload/dependencies.global.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
// Provides application-wide services.
|
||||
// We recommend using fully-qualified class names whenever possible as
|
||||
// service names.
|
||||
'dependencies' => [
|
||||
// Use 'aliases' to alias a service name to another service. The
|
||||
// key is the alias name, the value is the service to which it points.
|
||||
'aliases' => [
|
||||
// Fully\Qualified\ClassOrInterfaceName::class => Fully\Qualified\ClassName::class,
|
||||
],
|
||||
// Use 'invokables' for constructor-less services, or services that do
|
||||
// not require arguments to the constructor. Map a service name to the
|
||||
// class name.
|
||||
'invokables' => [
|
||||
// Fully\Qualified\InterfaceName::class => Fully\Qualified\ClassName::class,
|
||||
],
|
||||
// Use 'factories' for services provided by callbacks/factory classes.
|
||||
'factories' => [
|
||||
// Fully\Qualified\ClassName::class => Fully\Qualified\FactoryName::class,
|
||||
],
|
||||
],
|
||||
];
|
35
config/autoload/development.local.php.dist
Normal file
35
config/autoload/development.local.php.dist
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
// phpcs:disable PSR12.Files.FileHeader.IncorrectOrder
|
||||
|
||||
/**
|
||||
* Development-only configuration.
|
||||
*
|
||||
* Put settings you want enabled when under development mode in this file, and
|
||||
* check it into your repository.
|
||||
*
|
||||
* Developers on your team will then automatically enable them by calling on
|
||||
* `composer development-enable`.
|
||||
*/
|
||||
|
||||
use Mezzio\Container;
|
||||
use Mezzio\Middleware\ErrorResponseGenerator;
|
||||
|
||||
return [
|
||||
'dependencies' => [
|
||||
'factories' => [
|
||||
ErrorResponseGenerator::class => Container\WhoopsErrorResponseGeneratorFactory::class,
|
||||
'Mezzio\Whoops' => Container\WhoopsFactory::class,
|
||||
'Mezzio\WhoopsPageHandler' => Container\WhoopsPageHandlerFactory::class,
|
||||
],
|
||||
],
|
||||
'whoops' => [
|
||||
'json_exceptions' => [
|
||||
'display' => true,
|
||||
'show_trace' => true,
|
||||
'ajax_only' => true,
|
||||
],
|
||||
],
|
||||
];
|
13
config/autoload/local.php.dist
Normal file
13
config/autoload/local.php.dist
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Local configuration.
|
||||
*
|
||||
* Copy this file to `local.php` and change its settings as required.
|
||||
* `local.php` is ignored by git and safe to use for local and sensitive data like usernames and passwords.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
];
|
24
config/autoload/mezzio.global.php
Normal file
24
config/autoload/mezzio.global.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Laminas\ConfigAggregator\ConfigAggregator;
|
||||
|
||||
return [
|
||||
// Toggle the configuration cache. Set this to boolean false, or remove the
|
||||
// directive, to disable configuration caching. Toggling development mode
|
||||
// will also disable it by default; clear the configuration cache using
|
||||
// `composer clear-config-cache`.
|
||||
ConfigAggregator::ENABLE_CACHE => true,
|
||||
|
||||
// Enable debugging; typically used to provide debugging information within templates.
|
||||
'debug' => false,
|
||||
'mezzio' => [
|
||||
// Provide templates for the error handling middleware to use when
|
||||
// generating responses.
|
||||
'error_handler' => [
|
||||
'template_404' => 'error::404',
|
||||
'template_error' => 'error::error',
|
||||
],
|
||||
],
|
||||
];
|
42
config/config.php
Normal file
42
config/config.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Laminas\ConfigAggregator\ArrayProvider;
|
||||
use Laminas\ConfigAggregator\ConfigAggregator;
|
||||
use Laminas\ConfigAggregator\PhpFileProvider;
|
||||
use Mezzio\Helper\ConfigProvider;
|
||||
|
||||
// To enable or disable caching, set the `ConfigAggregator::ENABLE_CACHE` boolean in
|
||||
// `config/autoload/local.php`.
|
||||
$cacheConfig = [
|
||||
'config_cache_path' => 'data/cache/config-cache.php',
|
||||
];
|
||||
|
||||
$aggregator = new ConfigAggregator([
|
||||
\Mezzio\Tooling\ConfigProvider::class,
|
||||
\Mezzio\Plates\ConfigProvider::class,
|
||||
\Mezzio\Helper\ConfigProvider::class,
|
||||
\Mezzio\Router\FastRouteRouter\ConfigProvider::class,
|
||||
\Laminas\HttpHandlerRunner\ConfigProvider::class,
|
||||
// Include cache configuration
|
||||
new ArrayProvider($cacheConfig),
|
||||
ConfigProvider::class,
|
||||
\Mezzio\ConfigProvider::class,
|
||||
\Mezzio\Router\ConfigProvider::class,
|
||||
\Laminas\Diactoros\ConfigProvider::class,
|
||||
|
||||
\App\ConfigProvider::class,
|
||||
|
||||
// Load application config in a pre-defined order in such a way that local settings
|
||||
// overwrite global settings. (Loaded as first to last):
|
||||
// - `global.php`
|
||||
// - `*.global.php`
|
||||
// - `local.php`
|
||||
// - `*.local.php`
|
||||
new PhpFileProvider(realpath(__DIR__) . '/autoload/{{,*.}global,{,*.}local}.php'),
|
||||
// Load development config if it exists
|
||||
new PhpFileProvider(realpath(__DIR__) . '/development.config.php'),
|
||||
], $cacheConfig['config_cache_path']);
|
||||
|
||||
return $aggregator->getMergedConfig();
|
14
config/container.php
Normal file
14
config/container.php
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Laminas\ServiceManager\ServiceManager;
|
||||
|
||||
// Load configuration
|
||||
$config = require __DIR__ . '/config.php';
|
||||
|
||||
$dependencies = $config['dependencies'];
|
||||
$dependencies['services']['config'] = $config;
|
||||
|
||||
// Build container
|
||||
return new ServiceManager($dependencies);
|
31
config/development.config.php.dist
Normal file
31
config/development.config.php.dist
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* File required to allow enablement of development mode.
|
||||
*
|
||||
* For use with the laminas-development-mode tool.
|
||||
*
|
||||
* Usage:
|
||||
* $ composer development-disable
|
||||
* $ composer development-enable
|
||||
* $ composer development-status
|
||||
*
|
||||
* DO NOT MODIFY THIS FILE.
|
||||
*
|
||||
* Provide your own development-mode settings by editing the file
|
||||
* `config/autoload/development.local.php.dist`.
|
||||
*
|
||||
* Because this file is aggregated last, it simply ensures:
|
||||
*
|
||||
* - The `debug` flag is _enabled_.
|
||||
* - Configuration caching is _disabled_.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Laminas\ConfigAggregator\ConfigAggregator;
|
||||
|
||||
return [
|
||||
'debug' => true,
|
||||
ConfigAggregator::ENABLE_CACHE => false,
|
||||
];
|
77
config/pipeline.php
Normal file
77
config/pipeline.php
Normal file
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Laminas\Stratigility\Middleware\ErrorHandler;
|
||||
use Mezzio\Application;
|
||||
use Mezzio\Handler\NotFoundHandler;
|
||||
use Mezzio\Helper\ServerUrlMiddleware;
|
||||
use Mezzio\Helper\UrlHelperMiddleware;
|
||||
use Mezzio\MiddlewareFactory;
|
||||
use Mezzio\Router\Middleware\DispatchMiddleware;
|
||||
use Mezzio\Router\Middleware\ImplicitHeadMiddleware;
|
||||
use Mezzio\Router\Middleware\ImplicitOptionsMiddleware;
|
||||
use Mezzio\Router\Middleware\MethodNotAllowedMiddleware;
|
||||
use Mezzio\Router\Middleware\RouteMiddleware;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Setup middleware pipeline:
|
||||
*/
|
||||
|
||||
return function (Application $app, MiddlewareFactory $factory, ContainerInterface $container): void {
|
||||
// The error handler should be the first (most outer) middleware to catch
|
||||
// all Exceptions.
|
||||
$app->pipe(ErrorHandler::class);
|
||||
$app->pipe(ServerUrlMiddleware::class);
|
||||
|
||||
// Pipe more middleware here that you want to execute on every request:
|
||||
// - bootstrapping
|
||||
// - pre-conditions
|
||||
// - modifications to outgoing responses
|
||||
//
|
||||
// Piped Middleware may be either callables or service names. Middleware may
|
||||
// also be passed as an array; each item in the array must resolve to
|
||||
// middleware eventually (i.e., callable or service name).
|
||||
//
|
||||
// Middleware can be attached to specific paths, allowing you to mix and match
|
||||
// applications under a common domain. The handlers in each middleware
|
||||
// attached this way will see a URI with the matched path segment removed.
|
||||
//
|
||||
// i.e., path of "/api/member/profile" only passes "/member/profile" to $apiMiddleware
|
||||
// - $app->pipe('/api', $apiMiddleware);
|
||||
// - $app->pipe('/docs', $apiDocMiddleware);
|
||||
// - $app->pipe('/files', $filesMiddleware);
|
||||
|
||||
// Register the routing middleware in the middleware pipeline.
|
||||
// This middleware registers the Mezzio\Router\RouteResult request attribute.
|
||||
$app->pipe(RouteMiddleware::class);
|
||||
|
||||
// The following handle routing failures for common conditions:
|
||||
// - HEAD request but no routes answer that method
|
||||
// - OPTIONS request but no routes answer that method
|
||||
// - method not allowed
|
||||
// Order here matters; the MethodNotAllowedMiddleware should be placed
|
||||
// after the Implicit*Middleware.
|
||||
$app->pipe(ImplicitHeadMiddleware::class);
|
||||
$app->pipe(ImplicitOptionsMiddleware::class);
|
||||
$app->pipe(MethodNotAllowedMiddleware::class);
|
||||
|
||||
// Seed the UrlHelper with the routing results:
|
||||
$app->pipe(UrlHelperMiddleware::class);
|
||||
|
||||
// Add more middleware here that needs to introspect the routing results; this
|
||||
// might include:
|
||||
//
|
||||
// - route-based authentication
|
||||
// - route-based validation
|
||||
// - etc.
|
||||
|
||||
// Register the dispatch middleware in the middleware pipeline
|
||||
$app->pipe(DispatchMiddleware::class);
|
||||
|
||||
// At this point, if no Response is returned by any middleware, the
|
||||
// NotFoundHandler kicks in; alternately, you can provide other fallback
|
||||
// middleware to execute.
|
||||
$app->pipe(NotFoundHandler::class);
|
||||
};
|
29
config/routes.php
Normal file
29
config/routes.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Mezzio\Application;
|
||||
use Mezzio\MiddlewareFactory;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
/**
|
||||
* FastRoute route configuration
|
||||
*
|
||||
* @see https://github.com/nikic/FastRoute
|
||||
*
|
||||
* Setup routes with a single request method:
|
||||
*
|
||||
* $app->get('/', App\Handler\HomePageHandler::class, 'home');
|
||||
* $app->post('/album', App\Handler\AlbumCreateHandler::class, 'album.create');
|
||||
* $app->put('/album/{id:\d+}', App\Handler\AlbumUpdateHandler::class, 'album.put');
|
||||
* $app->patch('/album/{id:\d+}', App\Handler\AlbumUpdateHandler::class, 'album.patch');
|
||||
* $app->delete('/album/{id:\d+}', App\Handler\AlbumDeleteHandler::class, 'album.delete');
|
||||
*
|
||||
* Or with multiple request methods:
|
||||
*
|
||||
* $app->route('/contact', App\Handler\ContactHandler::class, ['GET', 'POST', ...], 'contact');
|
||||
*/
|
||||
|
||||
return static function (Application $app, MiddlewareFactory $factory, ContainerInterface $container): void {
|
||||
$app->get('/', \App\TestPage::class, 'app.home');
|
||||
};
|
4
data/.gitignore
vendored
Normal file
4
data/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
*
|
||||
!cache
|
||||
!cache/.gitkeep
|
||||
!.gitignore
|
0
data/cache/.gitkeep
vendored
Normal file
0
data/cache/.gitkeep
vendored
Normal file
59
docker-compose.yml
Normal file
59
docker-compose.yml
Normal file
|
@ -0,0 +1,59 @@
|
|||
version: '3'
|
||||
|
||||
services:
|
||||
nginx:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./docker/nginx/Dockerfile
|
||||
ports:
|
||||
- 80:80
|
||||
- 443:443
|
||||
volumes:
|
||||
- .:/var/www/html
|
||||
- ./docker/nginx/default:/etc/nginx/conf.d/default.conf
|
||||
networks:
|
||||
application:
|
||||
aliases:
|
||||
- 'application.app.local'
|
||||
|
||||
webapp:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/webapp/Dockerfile
|
||||
environment:
|
||||
- XDEBUG_CONFIG=idekey=PHPSTORM start_with_request=true var_display_max_depth=-1 max_nesting_level=3000
|
||||
- PHP_IDE_CONFIG=serverName=altimeter.local
|
||||
- XDEBUG_MODE=develop,debug,coverage
|
||||
- IN_TEST_MODE=true
|
||||
volumes:
|
||||
- .:/var/www/html
|
||||
networks:
|
||||
- application
|
||||
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
environment:
|
||||
- POSTGRES_PASSWORD=password
|
||||
- PGDATA=/opt/pgdata
|
||||
- POSTGRES_DB=application
|
||||
ports:
|
||||
- 5432:5432
|
||||
volumes:
|
||||
- postgresdata:/opt/pgdata
|
||||
networks:
|
||||
- application
|
||||
|
||||
node:
|
||||
image: node:19
|
||||
user: node
|
||||
working_dir: /home/node/app
|
||||
volumes:
|
||||
- .:/home/node/app
|
||||
|
||||
networks:
|
||||
application:
|
||||
driver: "bridge"
|
||||
|
||||
volumes:
|
||||
postgresdata:
|
||||
"driver": "local"
|
9
docker/nginx/Dockerfile
Normal file
9
docker/nginx/Dockerfile
Normal file
|
@ -0,0 +1,9 @@
|
|||
FROM nginx:latest
|
||||
|
||||
RUN apt-get update && apt install -y wget libnss3-tools \
|
||||
&& wget -O mkcert \
|
||||
https://github.com/FiloSottile/mkcert/releases/download/v1.4.3/mkcert-v1.4.3-linux-amd64 \
|
||||
&& chmod +x mkcert \
|
||||
&& mv mkcert /usr/local/bin \
|
||||
&& mkcert -install \
|
||||
&& mkcert *.application.local
|
41
docker/nginx/default
Normal file
41
docker/nginx/default
Normal file
|
@ -0,0 +1,41 @@
|
|||
server {
|
||||
listen 80 default_server;
|
||||
return 302 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
client_max_body_size 4M;
|
||||
server_name application.local;
|
||||
|
||||
ssl_certificate /_wildcard.application.local.pem;
|
||||
ssl_certificate_key /_wildcard.application.local-key.pem;
|
||||
|
||||
|
||||
root /var/www/html/public/;
|
||||
|
||||
index index.html index.htm index.php;
|
||||
|
||||
|
||||
charset utf-8;
|
||||
|
||||
location = /favicon.ico { log_not_found off; access_log off; }
|
||||
location = /robots.txt { log_not_found off; access_log off; }
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php$is_args$args;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
fastcgi_pass webapp:9000;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
include fastcgi_params;
|
||||
}
|
||||
|
||||
error_page 404 /index.php;
|
||||
|
||||
location ~ /\.ht {
|
||||
deny all;
|
||||
}
|
||||
}
|
18
docker/webapp/Dockerfile
Normal file
18
docker/webapp/Dockerfile
Normal file
|
@ -0,0 +1,18 @@
|
|||
FROM php:8.1-fpm
|
||||
|
||||
MAINTAINER Brandon Savage
|
||||
|
||||
RUN export DEBIAN_FRONTEND=noninteractive \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y -q \
|
||||
libpq-dev git zip libzip-dev libicu-dev
|
||||
|
||||
RUN docker-php-ext-install pdo \
|
||||
&& docker-php-ext-install pdo_pgsql \
|
||||
&& docker-php-ext-install zip \
|
||||
&& docker-php-ext-install intl \
|
||||
&& pecl install xdebug-3.1.4 \
|
||||
&& docker-php-ext-enable xdebug \
|
||||
&& docker-php-ext-enable pdo_pgsql
|
||||
|
||||
COPY --from=composer:2.1 /usr/bin/composer /usr/bin/composer
|
5
docker/webapp/php.ini
Normal file
5
docker/webapp/php.ini
Normal file
|
@ -0,0 +1,5 @@
|
|||
error_reporting=E_ALL & ~E_DEPRECATED
|
||||
|
||||
[xdebug]
|
||||
xdebug.client_host=host.docker.internal
|
||||
xdebug.mode=debug
|
0
logs/.gitkeep
Normal file
0
logs/.gitkeep
Normal file
2
logs/error.log
Normal file
2
logs/error.log
Normal file
File diff suppressed because one or more lines are too long
1368
package-lock.json
generated
Normal file
1368
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
6
package.json
Normal file
6
package.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"devDependencies": {
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"tailwindcss": "^3.2.4"
|
||||
}
|
||||
}
|
32
phpcs.xml.dist
Normal file
32
phpcs.xml.dist
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0"?>
|
||||
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="vendor/squizlabs/php_codesniffer/phpcs.xsd">
|
||||
|
||||
<arg name="basepath" value="."/>
|
||||
<arg name="cache" value=".phpcs-cache"/>
|
||||
<arg name="colors"/>
|
||||
<arg name="extensions" value="php"/>
|
||||
<arg name="parallel" value="80"/>
|
||||
|
||||
<!-- Show progress -->
|
||||
<arg value="p"/>
|
||||
|
||||
<!-- Paths to check -->
|
||||
<file>config</file>
|
||||
<file>src</file>
|
||||
<file>test</file>
|
||||
<exclude-pattern>config/config.php</exclude-pattern>
|
||||
<exclude-pattern>config/routes.php</exclude-pattern>
|
||||
|
||||
<!-- Include all rules from the Laminas Coding Standard -->
|
||||
<rule ref="LaminasCodingStandard"/>
|
||||
|
||||
<rule ref="Squiz.Classes.ClassFileName.NoMatch">
|
||||
<exclude-pattern>src/ConfigProvider.*.php</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="PSR12.Files.FileHeader.IncorrectOrder">
|
||||
<exclude-pattern>config/pipeline.php</exclude-pattern>
|
||||
<exclude-pattern>src/MezzioInstaller/Resources/config/routes-*.php</exclude-pattern>
|
||||
</rule>
|
||||
</ruleset>
|
5
phpstan.neon
Normal file
5
phpstan.neon
Normal file
|
@ -0,0 +1,5 @@
|
|||
parameters:
|
||||
level: 6
|
||||
paths:
|
||||
- src
|
||||
- tests
|
26
phpunit.xml.dist
Normal file
26
phpunit.xml.dist
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||
bootstrap="tests/bootstrap.php"
|
||||
executionOrder="depends,defects"
|
||||
beStrictAboutCoversAnnotation="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
beStrictAboutTodoAnnotatedTests="true"
|
||||
failOnRisky="true"
|
||||
failOnWarning="true"
|
||||
verbose="true"
|
||||
colors="true">
|
||||
<testsuites>
|
||||
<testsuite name="unit">
|
||||
<directory suffix="Test.php">tests/unit</directory>
|
||||
</testsuite>
|
||||
<testsuite name="functional">
|
||||
<directory suffix="Test.php">tests/functional</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<coverage processUncoveredFiles="true">
|
||||
<include>
|
||||
<directory suffix=".php">src</directory>
|
||||
</include>
|
||||
</coverage>
|
||||
</phpunit>
|
30
public/index.php
Normal file
30
public/index.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
// Delegate static file requests back to the PHP built-in webserver
|
||||
if (PHP_SAPI === 'cli-server' && $_SERVER['SCRIPT_FILENAME'] !== __FILE__) {
|
||||
return false;
|
||||
}
|
||||
|
||||
chdir(dirname(__DIR__));
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
/**
|
||||
* Self-called anonymous function that creates its own scope and keeps the global namespace clean.
|
||||
*/
|
||||
(function () {
|
||||
/** @var \Psr\Container\ContainerInterface $container */
|
||||
$container = require 'config/container.php';
|
||||
|
||||
/** @var \Mezzio\Application $app */
|
||||
$app = $container->get(\Mezzio\Application::class);
|
||||
$factory = $container->get(\Mezzio\MiddlewareFactory::class);
|
||||
|
||||
// Execute programmatic/declarative middleware pipeline and routing
|
||||
// configuration statements
|
||||
(require 'config/pipeline.php')($app, $factory, $container);
|
||||
(require 'config/routes.php')($app, $factory, $container);
|
||||
|
||||
$app->run();
|
||||
})();
|
1
public/style.css
Symbolic link
1
public/style.css
Symbolic link
|
@ -0,0 +1 @@
|
|||
../assets/output/style.css
|
29
src/ConfigProvider.php
Normal file
29
src/ConfigProvider.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use App\LoggingListenerDelegatorFactory;
|
||||
use Laminas\Stratigility\Middleware\ErrorHandler;
|
||||
|
||||
class ConfigProvider
|
||||
{
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function __invoke() : array
|
||||
{
|
||||
return [
|
||||
'dependencies' => [
|
||||
'delegators' => [
|
||||
ErrorHandler::class => [LoggingListenerDelegatorFactory::class],
|
||||
],
|
||||
'factories' => [
|
||||
TestPage::class => TestPageFactory::class,
|
||||
|
||||
// Logging Config
|
||||
'error' => LoggingFactory::class,
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
26
src/LoggingFactory.php
Normal file
26
src/LoggingFactory.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Laminas\ServiceManager\ServiceManager;
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Monolog\Logger;
|
||||
|
||||
class LoggingFactory
|
||||
{
|
||||
public function __invoke(ServiceManager $serviceManager, string $serviceName) : Logger
|
||||
{
|
||||
$config = $serviceManager->get('config');
|
||||
$loggingInfo = $config['logging'];
|
||||
|
||||
if (!isset($loggingInfo[$serviceName])) {
|
||||
throw new \InvalidArgumentException('Unknown service name: ' . $serviceName);
|
||||
}
|
||||
|
||||
$logConfig = $loggingInfo[$serviceName];
|
||||
|
||||
$log = new Logger($serviceName);
|
||||
$log->pushHandler(new StreamHandler($loggingInfo['path'] . $logConfig['file'], $logConfig['level']));
|
||||
return $log;
|
||||
}
|
||||
}
|
32
src/LoggingListener.php
Normal file
32
src/LoggingListener.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class LoggingListener
|
||||
{
|
||||
const LOG_FORMAT = '%d [%s] %s: %s';
|
||||
|
||||
private LoggerInterface $logger;
|
||||
|
||||
public function __construct(LoggerInterface $logger)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function __invoke(\Throwable $error, ServerRequestInterface $request, ResponseInterface $response) : void
|
||||
{
|
||||
$this->logger->error(
|
||||
sprintf(
|
||||
self::LOG_FORMAT,
|
||||
$response->getStatusCode(),
|
||||
$request->getMethod(),
|
||||
(string) $request->getUri(),
|
||||
(string)$error
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
20
src/LoggingListenerDelegatorFactory.php
Normal file
20
src/LoggingListenerDelegatorFactory.php
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Laminas\Stratigility\Middleware\ErrorHandler;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
class LoggingListenerDelegatorFactory
|
||||
{
|
||||
public function __invoke(ContainerInterface $container, string $name, callable $callback) : ErrorHandler
|
||||
{
|
||||
$logger = $container->get('error');
|
||||
|
||||
$listener = new LoggingListener($logger);
|
||||
/** @var ErrorHandler $errorHandler */
|
||||
$errorHandler = $callback();
|
||||
$errorHandler->attachListener($listener);
|
||||
return $errorHandler;
|
||||
}
|
||||
}
|
25
src/TestPage.php
Normal file
25
src/TestPage.php
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Laminas\Diactoros\Response\HtmlResponse;
|
||||
use Mezzio\Template\TemplateRendererInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
class TestPage implements RequestHandlerInterface
|
||||
{
|
||||
public function __construct(
|
||||
private TemplateRendererInterface $templateRenderer
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
public function handle(ServerRequestInterface $request) : ResponseInterface
|
||||
{
|
||||
return new HtmlResponse(
|
||||
$this->templateRenderer->render('app::sample', ['statement' => 'Hello world!'])
|
||||
);
|
||||
}
|
||||
}
|
15
src/TestPageFactory.php
Normal file
15
src/TestPageFactory.php
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Laminas\ServiceManager\ServiceManager;
|
||||
use Mezzio\Template\TemplateRendererInterface;
|
||||
|
||||
class TestPageFactory
|
||||
{
|
||||
public function __invoke(ServiceManager $serviceManager) : TestPage
|
||||
{
|
||||
$templateRenderer = $serviceManager->get(TemplateRendererInterface::class);
|
||||
return new TestPage($templateRenderer);
|
||||
}
|
||||
}
|
10
tailwind.config.js
Normal file
10
tailwind.config.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["./templates/**/*.php"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [
|
||||
require('@tailwindcss/forms'),
|
||||
],
|
||||
}
|
16
templates/app/sample.php
Normal file
16
templates/app/sample.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="style.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<h1 class="text-3xl font-bold underline">
|
||||
<?php
|
||||
|
||||
echo $statement;
|
||||
|
||||
?></h1>
|
||||
</body>
|
||||
</html>
|
8
tests/acceptance/container.php
Normal file
8
tests/acceptance/container.php
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Test\Acceptance;
|
||||
|
||||
$container = require 'config/container.php';
|
||||
return $container;
|
5
tests/acceptance/features/sample_feature.feature
Normal file
5
tests/acceptance/features/sample_feature.feature
Normal file
|
@ -0,0 +1,5 @@
|
|||
Feature: Sample feature
|
||||
|
||||
Scenario: True is in fact, true.
|
||||
Given that the world has not ended
|
||||
Then true still equals true
|
25
tests/acceptance/src/SampleContext.php
Normal file
25
tests/acceptance/src/SampleContext.php
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace AppTest\Acceptance;
|
||||
|
||||
use Behat\Behat\Context\Context;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
class SampleContext implements Context
|
||||
{
|
||||
/**
|
||||
* @Given that the world has not ended
|
||||
*/
|
||||
public function worldNotEnded() : void
|
||||
{
|
||||
Assert::eq(self::class, self::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then true still equals true
|
||||
*/
|
||||
public function sampleTestLine() : void
|
||||
{
|
||||
Assert::true(true);
|
||||
}
|
||||
}
|
6
tests/bootstrap.php
Normal file
6
tests/bootstrap.php
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?php
|
||||
// turn on all errors
|
||||
error_reporting(E_ALL);
|
||||
|
||||
// autoloader
|
||||
require ( dirname(__DIR__) . '/vendor/autoload.php');
|
13
tests/functional/src/SampleFunctionalTest.php
Normal file
13
tests/functional/src/SampleFunctionalTest.php
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace AppTest\Functional;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class SampleFunctionalTest extends TestCase
|
||||
{
|
||||
public function testSubtraction() : void
|
||||
{
|
||||
$this->assertEquals(2, (4-2));
|
||||
}
|
||||
}
|
13
tests/unit/src/SampleTest.php
Normal file
13
tests/unit/src/SampleTest.php
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace AppTest;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class SampleTest extends TestCase
|
||||
{
|
||||
public function testTwoPlusTwo() : void
|
||||
{
|
||||
$this->assertEquals(4, 2+2);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue