parent
75ece7ea27
commit
0b6d123b69
61 changed files with 6471 additions and 2694 deletions
@ -0,0 +1,12 @@ |
||||
/* |
||||
* Welcome to your app's main JavaScript file! |
||||
* |
||||
* We recommend including the built version of this JavaScript file |
||||
* (and its CSS file) in your base layout (base.html.twig). |
||||
*/ |
||||
|
||||
// any CSS you import will output into a single css file (app.css in this case)
|
||||
import './styles/app.css'; |
||||
|
||||
// start the Stimulus application
|
||||
import './bootstrap'; |
@ -0,0 +1,11 @@ |
||||
import { startStimulusApp } from '@symfony/stimulus-bridge'; |
||||
|
||||
// Registers Stimulus controllers from controllers.json and in the controllers/ directory
|
||||
export const app = startStimulusApp(require.context( |
||||
'@symfony/stimulus-bridge/lazy-controller-loader!./controllers', |
||||
true, |
||||
/\.[jt]sx?$/ |
||||
)); |
||||
|
||||
// register any custom, 3rd party controllers here
|
||||
// app.register('some_controller_name', SomeImportedController);
|
@ -0,0 +1,4 @@ |
||||
{ |
||||
"controllers": [], |
||||
"entrypoints": [] |
||||
} |
@ -0,0 +1,16 @@ |
||||
import { Controller } from '@hotwired/stimulus'; |
||||
|
||||
/* |
||||
* This is an example Stimulus controller! |
||||
* |
||||
* Any element with a data-controller="hello" attribute will cause |
||||
* this controller to be executed. The name "hello" comes from the filename: |
||||
* hello_controller.js -> "hello" |
||||
* |
||||
* Delete this file or adapt it for your use! |
||||
*/ |
||||
export default class extends Controller { |
||||
connect() { |
||||
this.element.textContent = 'Hello Stimulus! Edit me in assets/controllers/hello_controller.js'; |
||||
} |
||||
} |
@ -0,0 +1,3 @@ |
||||
body { |
||||
background-color: lightgray; |
||||
} |
@ -0,0 +1,17 @@ |
||||
#!/usr/bin/env php |
||||
<?php |
||||
|
||||
use App\Kernel; |
||||
use Symfony\Bundle\FrameworkBundle\Console\Application; |
||||
|
||||
if (!is_file(dirname(__DIR__).'/vendor/autoload_runtime.php')) { |
||||
throw new LogicException('Symfony Runtime is missing. Try running "composer require symfony/runtime".'); |
||||
} |
||||
|
||||
require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; |
||||
|
||||
return function (array $context) { |
||||
$kernel = new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); |
||||
|
||||
return new Application($kernel); |
||||
}; |
@ -1,11 +0,0 @@ |
||||
<?php |
||||
|
||||
require_once "vendor/autoload.php"; |
||||
|
||||
if( ini_get('safe_mode') ){ |
||||
die("Cannot run in safe mode"); |
||||
} |
||||
|
||||
if (!file_exists(__DIR__ . "/.env")) { |
||||
die ("Cannot find config file"); |
||||
} |
@ -1,40 +1,87 @@ |
||||
{ |
||||
"name": "jeroened/webcron", |
||||
"description": "A simple webapp to manage webcrons", |
||||
"authors": [ |
||||
{ |
||||
"name": "Jeroen De Meerleer", |
||||
"email": "me@jeroened.be" |
||||
} |
||||
], |
||||
"type": "project", |
||||
"license": "proprietary", |
||||
"minimum-stability": "stable", |
||||
"prefer-stable": true, |
||||
"require": { |
||||
"doctrine/dbal": "~3.2.0", |
||||
"php": ">=8.1", |
||||
"ext-ctype": "*", |
||||
"ext-iconv": "*", |
||||
"ext-intl": "*", |
||||
"ext-openssl": "*", |
||||
"ext-pcntl": "*", |
||||
"doctrine/doctrine-bundle": "^2.6", |
||||
"doctrine/doctrine-migrations-bundle": "^3.2", |
||||
"doctrine/orm": "^2.12", |
||||
"guzzlehttp/guzzle": "~7.4.0", |
||||
"mehrkanal/twig-encore-extension": "~1.3.0", |
||||
"phpseclib/phpseclib": "^3.0", |
||||
"symfony/config": "~6.0.0", |
||||
"symfony/console": "~6.0.0", |
||||
"symfony/dotenv": "~6.0.0", |
||||
"symfony/http-foundation": "~6.0.0", |
||||
"symfony/mailer": "~6.0.0", |
||||
"symfony/routing": "~6.0.0", |
||||
"symfony/yaml": "~6.0.0", |
||||
"twig/intl-extra": "~3.3.0", |
||||
"twig/twig": "~3.3.0", |
||||
"ext-pcntl": "*", |
||||
"ext-intl": "*", |
||||
"ext-openssl": "*" |
||||
"symfony/console": "6.0.*", |
||||
"symfony/dotenv": "6.0.*", |
||||
"symfony/flex": "^2", |
||||
"symfony/framework-bundle": "6.0.*", |
||||
"symfony/proxy-manager-bridge": "6.0.*", |
||||
"symfony/runtime": "6.0.*", |
||||
"symfony/security-bundle": "6.0.*", |
||||
"symfony/twig-bundle": "6.0.*", |
||||
"symfony/webpack-encore-bundle": "^1.14", |
||||
"symfony/yaml": "6.0.*" |
||||
}, |
||||
"config": { |
||||
"allow-plugins": { |
||||
"composer/package-versions-deprecated": true, |
||||
"symfony/flex": true, |
||||
"symfony/runtime": true |
||||
}, |
||||
"optimize-autoloader": true, |
||||
"preferred-install": { |
||||
"*": "dist" |
||||
}, |
||||
"sort-packages": true |
||||
}, |
||||
"autoload": { |
||||
"psr-4": { |
||||
"JeroenED\\Framework\\": "lib/Framework/", |
||||
"JeroenED\\Webcron\\": "src/" |
||||
"App\\": "src/" |
||||
} |
||||
}, |
||||
"config": { |
||||
"sort-packages": true, |
||||
"allow-plugins": { |
||||
"composer/package-versions-deprecated": true |
||||
"autoload-dev": { |
||||
"psr-4": { |
||||
"App\\Tests\\": "tests/" |
||||
} |
||||
}, |
||||
"replace": { |
||||
"symfony/polyfill-ctype": "*", |
||||
"symfony/polyfill-iconv": "*", |
||||
"symfony/polyfill-php72": "*", |
||||
"symfony/polyfill-php73": "*", |
||||
"symfony/polyfill-php74": "*", |
||||
"symfony/polyfill-php80": "*" |
||||
}, |
||||
"scripts": { |
||||
"auto-scripts": { |
||||
"cache:clear": "symfony-cmd", |
||||
"assets:install %PUBLIC_DIR%": "symfony-cmd" |
||||
}, |
||||
"post-install-cmd": [ |
||||
"@auto-scripts" |
||||
], |
||||
"post-update-cmd": [ |
||||
"@auto-scripts" |
||||
] |
||||
}, |
||||
"conflict": { |
||||
"symfony/symfony": "*" |
||||
}, |
||||
"extra": { |
||||
"symfony": { |
||||
"allow-contrib": false, |
||||
"require": "6.0.*" |
||||
} |
||||
}, |
||||
"require-dev": { |
||||
"symfony/debug-bundle": "6.0.*", |
||||
"symfony/maker-bundle": "^1.40", |
||||
"symfony/monolog-bundle": "^3.0", |
||||
"symfony/stopwatch": "6.0.*", |
||||
"symfony/web-profiler-bundle": "6.0.*" |
||||
} |
||||
} |
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,14 @@ |
||||
<?php |
||||
|
||||
return [ |
||||
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], |
||||
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], |
||||
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], |
||||
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], |
||||
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], |
||||
Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true], |
||||
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], |
||||
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], |
||||
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], |
||||
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true], |
||||
]; |
@ -0,0 +1,19 @@ |
||||
framework: |
||||
cache: |
||||
# Unique name of your app: used to compute stable namespaces for cache keys. |
||||
#prefix_seed: your_vendor_name/app_name |
||||
|
||||
# The "app" cache stores to the filesystem by default. |
||||
# The data in this cache should persist between deploys. |
||||
# Other options include: |
||||
|
||||
# Redis |
||||
#app: cache.adapter.redis |
||||
#default_redis_provider: redis://localhost |
||||
|
||||
# APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues) |
||||
#app: cache.adapter.apcu |
||||
|
||||
# Namespaced pools use the above "app" backend by default |
||||
#pools: |
||||
#my.dedicated.cache: null |
@ -0,0 +1,5 @@ |
||||
when@dev: |
||||
debug: |
||||
# Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser. |
||||
# See the "server:dump" command to start a new server. |
||||
dump_destination: "tcp://%env(VAR_DUMPER_SERVER)%" |
@ -0,0 +1,42 @@ |
||||
doctrine: |
||||
dbal: |
||||
url: '%env(resolve:DATABASE_URL)%' |
||||
|
||||
# IMPORTANT: You MUST configure your server version, |
||||
# either here or in the DATABASE_URL env var (see .env file) |
||||
#server_version: '13' |
||||
orm: |
||||
auto_generate_proxy_classes: true |
||||
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware |
||||
auto_mapping: true |
||||
mappings: |
||||
App: |
||||
is_bundle: false |
||||
dir: '%kernel.project_dir%/src/Entity' |
||||
prefix: 'App\Entity' |
||||
alias: App |
||||
|
||||
when@test: |
||||
doctrine: |
||||
dbal: |
||||
# "TEST_TOKEN" is typically set by ParaTest |
||||
dbname_suffix: '_test%env(default::TEST_TOKEN)%' |
||||
|
||||
when@prod: |
||||
doctrine: |
||||
orm: |
||||
auto_generate_proxy_classes: false |
||||
query_cache_driver: |
||||
type: pool |
||||
pool: doctrine.system_cache_pool |
||||
result_cache_driver: |
||||
type: pool |
||||
pool: doctrine.result_cache_pool |
||||
|
||||
framework: |
||||
cache: |
||||
pools: |
||||
doctrine.result_cache_pool: |
||||
adapter: cache.app |
||||
doctrine.system_cache_pool: |
||||
adapter: cache.system |
@ -0,0 +1,6 @@ |
||||
doctrine_migrations: |
||||
migrations_paths: |
||||
# namespace is arbitrary but should be different from App\Migrations |
||||
# as migrations classes should NOT be autoloaded |
||||
'DoctrineMigrations': '%kernel.project_dir%/migrations' |
||||
enable_profiler: '%kernel.debug%' |
@ -0,0 +1,24 @@ |
||||
# see https://symfony.com/doc/current/reference/configuration/framework.html |
||||
framework: |
||||
secret: '%env(APP_SECRET)%' |
||||
#csrf_protection: true |
||||
http_method_override: false |
||||
|
||||
# Enables session support. Note that the session will ONLY be started if you read or write from it. |
||||
# Remove or comment this section to explicitly disable session support. |
||||
session: |
||||
handler_id: null |
||||
cookie_secure: auto |
||||
cookie_samesite: lax |
||||
storage_factory_id: session.storage.factory.native |
||||
|
||||
#esi: true |
||||
#fragments: true |
||||
php_errors: |
||||
log: true |
||||
|
||||
when@test: |
||||
framework: |
||||
test: true |
||||
session: |
||||
storage_factory_id: session.storage.factory.mock_file |
@ -0,0 +1,61 @@ |
||||
monolog: |
||||
channels: |
||||
- deprecation # Deprecations are logged in the dedicated "deprecation" channel when it exists |
||||
|
||||
when@dev: |
||||
monolog: |
||||
handlers: |
||||
main: |
||||
type: stream |
||||
path: "%kernel.logs_dir%/%kernel.environment%.log" |
||||
level: debug |
||||
channels: ["!event"] |
||||
# uncomment to get logging in your browser |
||||
# you may have to allow bigger header sizes in your Web server configuration |
||||
#firephp: |
||||
# type: firephp |
||||
# level: info |
||||
#chromephp: |
||||
# type: chromephp |
||||
# level: info |
||||
console: |
||||
type: console |
||||
process_psr_3_messages: false |
||||
channels: ["!event", "!doctrine", "!console"] |
||||
|
||||
when@test: |
||||
monolog: |
||||
handlers: |
||||
main: |
||||
type: fingers_crossed |
||||
action_level: error |
||||
handler: nested |
||||
excluded_http_codes: [404, 405] |
||||
channels: ["!event"] |
||||
nested: |
||||
type: stream |
||||
path: "%kernel.logs_dir%/%kernel.environment%.log" |
||||
level: debug |
||||
|
||||
when@prod: |
||||
monolog: |
||||
handlers: |
||||
main: |
||||
type: fingers_crossed |
||||
action_level: error |
||||
handler: nested |
||||
excluded_http_codes: [404, 405] |
||||
buffer_size: 50 # How many messages should be saved? Prevent memory leaks |
||||
nested: |
||||
type: stream |
||||
path: php://stderr |
||||
level: debug |
||||
formatter: monolog.formatter.json |
||||
console: |
||||
type: console |
||||
process_psr_3_messages: false |
||||
channels: ["!event", "!doctrine"] |
||||
deprecation: |
||||
type: stream |
||||
channels: [deprecation] |
||||
path: php://stderr |
@ -0,0 +1,12 @@ |
||||
framework: |
||||
router: |
||||
utf8: true |
||||
|
||||
# Configure how to generate URLs in non-HTTP contexts, such as CLI commands. |
||||
# See https://symfony.com/doc/current/routing.html#generating-urls-in-commands |
||||
#default_uri: http://localhost |
||||
|
||||
when@prod: |
||||
framework: |
||||
router: |
||||
strict_requirements: null |
@ -0,0 +1,42 @@ |
||||
security: |
||||
enable_authenticator_manager: true |
||||
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords |
||||
password_hashers: |
||||
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' |
||||
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider |
||||
providers: |
||||
app_user_provider: |
||||
entity: |
||||
class: App\Entity\User |
||||
property: email |
||||
firewalls: |
||||
dev: |
||||
pattern: ^/(_(profiler|wdt)|css|images|js|health)/ |
||||
security: false |
||||
main: |
||||
pattern: ^\/(.*) |
||||
provider: app_user_provider |
||||
|
||||
form_login: |
||||
login_path: /login |
||||
check_path: /login_check |
||||
enable_csrf: true |
||||
logout: |
||||
path: /logout |
||||
target: / |
||||
remember_me: |
||||
secret: '%kernel.secret%' |
||||
lifetime: 2419200 # 28 days in seconds |
||||
path: / |
||||
secure: true |
||||
# activate different ways to authenticate |
||||
# https://symfony.com/doc/current/security.html#the-firewall |
||||
|
||||
# https://symfony.com/doc/current/security/impersonating_user.html |
||||
# switch_user: true |
||||
|
||||
# Easy way to control access for large sections of your site |
||||
# Note: Only the *first* access control that matches will be used |
||||
access_control: |
||||
- { path: ^/(?!login|login_check|health)(?=.*), roles: ROLE_USER } |
||||
# - { path: ^/profile, roles: ROLE_USER } |
@ -0,0 +1,6 @@ |
||||
twig: |
||||
default_path: '%kernel.project_dir%/templates' |
||||
|
||||
when@test: |
||||
twig: |
||||
strict_variables: true |
@ -0,0 +1,15 @@ |
||||
when@dev: |
||||
web_profiler: |
||||
toolbar: true |
||||
intercept_redirects: false |
||||
|
||||
framework: |
||||
profiler: { only_exceptions: false } |
||||
|
||||
when@test: |
||||
web_profiler: |
||||
toolbar: false |
||||
intercept_redirects: false |
||||
|
||||
framework: |
||||
profiler: { collect: false } |
@ -0,0 +1,49 @@ |
||||
webpack_encore: |
||||
# The path where Encore is building the assets - i.e. Encore.setOutputPath() |
||||
output_path: '%kernel.project_dir%/public/build' |
||||
# If multiple builds are defined (as shown below), you can disable the default build: |
||||
# output_path: false |
||||
|
||||
# Set attributes that will be rendered on all script and link tags |
||||
script_attributes: |
||||
defer: true |
||||
# Uncomment (also under link_attributes) if using Turbo Drive |
||||
# https://turbo.hotwired.dev/handbook/drive#reloading-when-assets-change |
||||
# 'data-turbo-track': reload |
||||
# link_attributes: |
||||
# Uncomment if using Turbo Drive |
||||
# 'data-turbo-track': reload |
||||
|
||||
# If using Encore.enableIntegrityHashes() and need the crossorigin attribute (default: false, or use 'anonymous' or 'use-credentials') |
||||
# crossorigin: 'anonymous' |
||||
|
||||
# Preload all rendered script and link tags automatically via the HTTP/2 Link header |
||||
# preload: true |
||||
|
||||
# Throw an exception if the entrypoints.json file is missing or an entry is missing from the data |
||||
# strict_mode: false |
||||
|
||||
# If you have multiple builds: |
||||
# builds: |
||||
# pass "frontend" as the 3rg arg to the Twig functions |
||||
# {{ encore_entry_script_tags('entry1', null, 'frontend') }} |
||||
|
||||
# frontend: '%kernel.project_dir%/public/frontend/build' |
||||
|
||||
# Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes) |
||||
# Put in config/packages/prod/webpack_encore.yaml |
||||
# cache: true |
||||
|
||||
framework: |
||||
assets: |
||||
json_manifest_path: '%kernel.project_dir%/public/build/manifest.json' |
||||
|
||||
#when@prod: |
||||
# webpack_encore: |
||||
# # Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes) |
||||
# # Available in version 1.2 |
||||
# cache: true |
||||
|
||||
#when@test: |
||||
# webpack_encore: |
||||
# strict_mode: false |
@ -0,0 +1,5 @@ |
||||
<?php |
||||
|
||||
if (file_exists(dirname(__DIR__).'/var/cache/prod/App_KernelProdContainer.preload.php')) { |
||||
require dirname(__DIR__).'/var/cache/prod/App_KernelProdContainer.preload.php'; |
||||
} |
@ -0,0 +1,4 @@ |
||||
when@dev: |
||||
_errors: |
||||
resource: '@FrameworkBundle/Resources/config/routing/errors.xml' |
||||
prefix: /_error |
@ -0,0 +1,8 @@ |
||||
when@dev: |
||||
web_profiler_wdt: |
||||
resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml' |
||||
prefix: /_wdt |
||||
|
||||
web_profiler_profiler: |
||||
resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml' |
||||
prefix: /_profiler |
@ -0,0 +1,24 @@ |
||||
# This file is the entry point to configure your own services. |
||||
# Files in the packages/ subdirectory configure your dependencies. |
||||
|
||||
# Put parameters here that don't need to change on each machine where the app is deployed |
||||
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration |
||||
parameters: |
||||
|
||||
services: |
||||
# default configuration for services in *this* file |
||||
_defaults: |
||||
autowire: true # Automatically injects dependencies in your services. |
||||
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. |
||||
|
||||
# makes classes in src/ available to be used as services |
||||
# this creates a service per class whose id is the fully-qualified class name |
||||
App\: |
||||
resource: '../src/' |
||||
exclude: |
||||
- '../src/DependencyInjection/' |
||||
- '../src/Entity/' |
||||
- '../src/Kernel.php' |
||||
|
||||
# add more service definitions when explicit configuration is needed |
||||
# please note that last definitions always *replace* previous ones |
@ -1,71 +0,0 @@ |
||||
<?php |
||||
|
||||
namespace JeroenED\Framework; |
||||
|
||||
|
||||
use Doctrine\DBAL\Connection; |
||||
use Symfony\Component\HttpFoundation\Request; |
||||
use Symfony\Component\HttpFoundation\Response; |
||||
use Twig\Environment; |
||||
use Twig\Loader\FilesystemLoader; |
||||
|
||||
abstract class Controller |
||||
{ |
||||
private $twig; |
||||
private $request; |
||||
private $database; |
||||
private $kernel; |
||||
|
||||
public function __construct(Request $request, Kernel $kernel) |
||||
{ |
||||
$this->twig = new Twig($kernel); |
||||
$this->request = $request; |
||||
$this->kernel = $kernel; |
||||
} |
||||
|
||||
public function getDbCon(): Connection |
||||
{ |
||||
return $this->kernel->getDbCon(); |
||||
} |
||||
|
||||
/** |
||||
* @return Request |
||||
*/ |
||||
public function getRequest(): Request |
||||
{ |
||||
return $this->request; |
||||
} |
||||
|
||||
/** |
||||
* @param Request $request |
||||
*/ |
||||
public function setRequest(Request $request): void |
||||
{ |
||||
$this->request = $request; |
||||
} |
||||
|
||||
/** |
||||
* @param string $template |
||||
* @param array $vars |
||||
* @return Response |
||||
*/ |
||||
public function render(string $template, array $vars = []): Response |
||||
{ |
||||
if(empty($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) != 'xmlhttprequest') { |
||||
$vars['flashes'] = $_SESSION['flashes'] ?? [] ; |
||||
$_SESSION['flashes'] = []; |
||||
} |
||||
$response = new Response($this->twig->render($template, $vars)); |
||||
return $response; |
||||
} |
||||
|
||||
public function generateRoute(string $route): string |
||||
{ |
||||
return $this->kernel->getRouter()->getUrlForRoute($route); |
||||
} |
||||
|
||||
public function addFlash(string $category, string $content): void |
||||
{ |
||||
$_SESSION['flashes'][] = ['category' => $category, 'content' => $content]; |
||||
} |
||||
} |
@ -1,141 +0,0 @@ |
||||
<?php |
||||
|
||||
namespace JeroenED\Framework; |
||||
|
||||
|
||||
use Doctrine\DBAL\Connection; |
||||
use Doctrine\DBAL\DriverManager; |
||||
use http\Exception\InvalidArgumentException; |
||||
use Symfony\Component\Config\FileLocator; |
||||
use Symfony\Component\Routing\Loader\YamlFileLoader; |
||||
use Symfony\Component\HttpFoundation\Request; |
||||
use Symfony\Component\HttpFoundation\Response; |
||||
use Symfony\Component\Routing\RouteCollection; |
||||
use Symfony\Component\Routing\RequestContext; |
||||
use Symfony\Component\Routing\Matcher\UrlMatcher; |
||||
use Symfony\Component\Dotenv\Dotenv; |
||||
|
||||
class Kernel |
||||
{ |
||||
private string $configDir; |
||||
private string $projectDir; |
||||
private string $templateDir; |
||||
private string $cacheDir; |
||||
private Router $router; |
||||
private ?Connection $dbCon = NULL; |
||||
|
||||
/** |
||||
* @return string |
||||
*/ |
||||
public function getConfigDir(): string |
||||
{ |
||||
return $this->configDir; |
||||
} |
||||
|
||||
/** |
||||
* @param string $configDir |
||||
*/ |
||||
public function setConfigDir(string $configDir): void |
||||
{ |
||||
$this->configDir = $configDir; |
||||
} |
||||
|
||||
/** |
||||
* @return string |
||||
*/ |
||||
public function getProjectDir(): string |
||||
{ |
||||
return $this->projectDir; |
||||
} |
||||
|
||||
/** |
||||
* @param string $projectDir |
||||
*/ |
||||
public function setProjectDir(string $projectDir): void |
||||
{ |
||||
$this->projectDir = $projectDir; |
||||
} |
||||
|
||||
/** |
||||
* @return string |
||||
*/ |
||||
public function getTemplateDir(): string |
||||
{ |
||||
return $this->templateDir; |
||||
} |
||||
|
||||
/** |
||||
* @param string $templateDir |
||||
*/ |
||||
public function setTemplateDir(string $templateDir): void |
||||
{ |
||||
$this->templateDir = $templateDir; |
||||
} |
||||
|
||||
/** |
||||
* @return string |
||||
*/ |
||||
public function getCacheDir(): string |
||||
{ |
||||
return $this->cacheDir; |
||||
} |
||||
|
||||
/** |
||||
* @param string $cacheDir |
||||
*/ |
||||
public function setCacheDir(string $cacheDir): void |
||||
{ |
||||
$this->cacheDir = $cacheDir; |
||||
} |
||||
|
||||
/** |
||||
* @return Router |
||||
*/ |
||||
public function getRouter(): Router |
||||
{ |
||||
return $this->router; |
||||
} |
||||
|
||||
public function handle(): Response |
||||
{ |
||||
$this->router = new Router(); |
||||
$this->router->parseRoutes($this->getConfigDir(), 'routes.yaml'); |
||||
$request = $this->parseRequest(); |
||||
|
||||
if($request->isSecure()) { |
||||
ini_set('session.cookie_httponly', true); |
||||
ini_set('session.cookie_secure', true); |
||||
} |
||||
|
||||
session_start(); |
||||
return $this->router->route($request, $this); |
||||
} |
||||
|
||||
public function parseDotEnv(string $path): void |
||||
{ |
||||
$dotenv = new Dotenv(); |
||||
$dotenv->loadEnv($path); |
||||
} |
||||
|
||||
private function parseRequest(): Request |
||||
{ |
||||
Request::setTrustedProxies(explode(',', $_ENV['TRUSTED_PROXIES']), Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO); |
||||
$request = Request::createFromGlobals(); |
||||
return $request; |
||||
} |
||||
|
||||
public function getNewDbCon(): Connection { |
||||
if(!is_null($this->dbCon)) { |
||||
$this->dbCon->close(); |
||||
$this->dbCon = null; |
||||
} |
||||
$this->dbCon = DriverManager::getConnection(['url' => $_ENV['DATABASE']]); |
||||
return $this->dbCon; |
||||
} |
||||
|
||||
public function getDbCon(): Connection |
||||
{ |
||||
if(is_null($this->dbCon)) $this->dbCon = DriverManager::getConnection(['url' => $_ENV['DATABASE']]); |
||||
return $this->dbCon; |
||||
} |
||||
} |
@ -1,17 +0,0 @@ |
||||
<?php |
||||
|
||||
|
||||
namespace JeroenED\Framework; |
||||
|
||||
|
||||
use Doctrine\DBAL\Connection; |
||||
|
||||
class Repository |
||||
{ |
||||
protected Connection $dbcon; |
||||
|
||||
public function __construct(Connection $dbcon) |
||||
{ |
||||
$this->dbcon = $dbcon; |
||||
} |
||||
} |
@ -1,57 +0,0 @@ |
||||
<?php |
||||
|
||||
|
||||
namespace JeroenED\Framework; |
||||
|
||||
|
||||
use Symfony\Component\Config\FileLocator; |
||||
use Symfony\Component\Filesystem\Exception\InvalidArgumentException; |
||||
use Symfony\Component\HttpFoundation\Request; |
||||
use Symfony\Component\HttpFoundation\Response; |
||||
use Symfony\Component\Routing\Generator\UrlGenerator; |
||||
use Symfony\Component\Routing\Loader\YamlFileLoader; |
||||
use Symfony\Component\Routing\Matcher\UrlMatcher; |
||||
use Symfony\Component\Routing\RequestContext; |
||||
use Symfony\Component\Routing\RouteCollection; |
||||
|
||||
class Router |
||||
{ |
||||
private RouteCollection $routes; |
||||
private RequestContext $requestContext; |
||||
|
||||
public function route(Request $request, Kernel $kernel): Response |
||||
{ |
||||
$requestContext = new RequestContext(); |
||||
$this->requestContext = $requestContext->fromRequest($request); |
||||
$matcher = new UrlMatcher($this->routes, $this->requestContext); |
||||
$method = $matcher->match($request->getPathInfo()); |
||||
$controller = explode('::', $method['_controller']); |
||||
$controllerObj = new ('\\' . $controller[0])($request, $kernel); |
||||
$action = $controller[1]; |
||||
unset($method['_controller']); |
||||
unset($method['_route']); |
||||
$response = $controllerObj->$action(...$method); |
||||
|
||||
if ($response instanceof Response) { |
||||
$response->headers->add([ |
||||
"Content-Security-Policy" => "default-src 'none'; font-src 'self'; style-src 'self'; script-src 'self'; connect-src 'self'; img-src 'self' data:; form-action 'self'; require-trusted-types-for 'script'; frame-ancestors 'none'; base-uri 'none'", |
||||
"Referrer-Policy" => "same-origin" |
||||
]); |
||||
return $response; |
||||
} else { |
||||
throw new InvalidArgumentException(); |
||||
} |
||||
} |
||||
|
||||
public function parseRoutes(string $dir, string $file): void |
||||
{ |
||||
$routeloader = new YamlFileLoader(new FileLocator($dir)); |
||||
$this->routes = $routeloader->load($file); |
||||
} |
||||
|
||||
public function getUrlForRoute(string $route, array $params = []): string |
||||
{ |
||||
$matcher = new UrlGenerator($this->routes, $this->requestContext); |
||||
return $matcher->generate($route, $params, UrlGenerator::ABSOLUTE_URL); |
||||
} |
||||
} |
@ -1,114 +0,0 @@ |
||||
<?php |
||||
|
||||
|
||||
namespace JeroenED\Framework; |
||||
|
||||
|
||||
use Mehrkanal\EncoreTwigExtension\Extensions\EntryFilesTwigExtension; |
||||
use Symfony\WebpackEncoreBundle\Asset\EntrypointLookup; |
||||
use Twig\Cache\FilesystemCache; |
||||
use Twig\Environment; |
||||
use Twig\Extra\Intl\IntlExtension; |
||||
use Twig\Loader\FilesystemLoader; |
||||
use Twig\TwigFilter; |
||||
use Twig\TwigFunction; |
||||
|
||||
class Twig |
||||
{ |
||||
private Environment $environment; |
||||
private Kernel $kernel; |
||||
|
||||
public function __construct(Kernel $kernel) |
||||
{ |
||||
$loader = new FilesystemLoader([$kernel->getTemplateDir()]); |
||||
$this->environment = new Environment($loader); |
||||
|
||||
if($_ENV['DEBUG'] != 'true') { |
||||
$cache = new FilesystemCache($kernel->getCacheDir() . '/twig'); |
||||
$this->environment->setCache($cache); |
||||
} |
||||
|
||||
$this->kernel = $kernel; |
||||
$this->addExtensions(); |
||||
$this->addFunctions(); |
||||
$this->addFilters(); |
||||
} |
||||
|
||||
public function render(string $template, array $vars = []): string |
||||
{ |
||||
return $this->environment->render($template, $vars); |
||||
} |
||||
|
||||
public function addExtensions() |
||||
{ |
||||
$this->environment->addExtension(new IntlExtension()); |
||||
$this->environment->addExtension(new EntryFilesTwigExtension(new EntrypointLookup('./public/build/entrypoints.json'))); |
||||
} |
||||
public function addFunctions() |
||||
{ |
||||
$path = new TwigFunction('path', function(string $route, array $params = []) { |
||||
return $this->kernel->getRouter()->getUrlForRoute($route, $params); |
||||
}); |
||||
$this->environment->addFunction($path); |
||||
|
||||
} |
||||
|
||||
public function addFilters() { |
||||
$secondsToInterval = new TwigFilter('interval', function(int|float $time) { |
||||
$return = ''; |
||||
|
||||
$days = floor($time / (60 * 60 * 24)); |
||||
$time -= $days * (60 * 60 * 24); |
||||
$return .= ($days != 0 || !empty($return)) ? "{$days}d " : ''; |
||||
|
||||
$hours = floor($time / (60 * 60)); |
||||
$time -= $hours * (60 * 60); |
||||
$return .= ($hours != 0 || !empty($return)) ? "{$hours}h " : ''; |
||||
|
||||
$minutes = floor($time / 60); |
||||
$time -= $minutes * 60; |
||||
$return .= ($minutes != 0 || !empty($return)) ? "{$minutes}m " : ''; |
||||
|
||||
$time = round($time, 3); |
||||
$return .= ($time != 0 || !empty($return)) ? "{$time}s " : ''; |
||||
|
||||
return $return; |
||||
}); |
||||
$parseTags = new TwigFilter('parsetags', function(string $text) { |
||||
$results = []; |
||||
preg_match_all('/\[([A-Za-z0-9 \-]+)\]/', $text, $results); |
||||
foreach ($results[0] as $key=>$result) { |
||||
$background = substr(md5($results[0][$key]), 0, 6); |
||||
$color = $this->lightOrDark($background) == 'dark' ? 'ffffff' : '000000'; |
||||
$text = str_replace($results[0][$key], '<span class="tag" data-background-color="#' . $background . '" data-color="#' . $color . '">' . $results[1][$key] . '</span>', $text); |
||||
} |
||||
return $text; |
||||
}); |
||||
|
||||
$this->environment->addFilter($secondsToInterval); |
||||
$this->environment->addFilter($parseTags); |
||||
|
||||
} |
||||
|
||||
private function lightOrDark ($color) { |
||||
$color = str_split($color, 2); |
||||
foreach($color as &$value) { |
||||
$value = hexdec($value); |
||||
} |
||||
|
||||
// HSP (Highly Sensitive Poo) equation from http://alienryderflex.com/hsp.html |
||||
$hsp = sqrt( |
||||
0.299 * ($color[0] * $color[0]) + |
||||
0.587 * ($color[1] * $color[1]) + |
||||
0.114 * ($color[2] * $color[2]) |
||||
); |
||||
|
||||
|
||||
// Using the HSP value, determine whether the color is light or dark |
||||
if ($hsp>140) { |
||||
return 'light'; |
||||
} else { |
||||
return 'dark'; |
||||
} |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -1,18 +1,9 @@ |
||||
<?php |
||||
error_reporting(E_ALL); |
||||
ini_set('display_errors', true); |
||||
|
||||
use JeroenED\Framework\Kernel; |
||||
chdir(__DIR__ . '/..'); |
||||
require_once 'bootstrap.php'; |
||||
use App\Kernel; |
||||
|
||||
$kernel = new Kernel(); |
||||
$kernel->setProjectDir(getcwd()); |
||||
$kernel->setConfigDir(getcwd() . '/config/'); |
||||
$kernel->setTemplateDir(getcwd() . '/templates/'); |
||||
$kernel->setCacheDir(getcwd() . '/cache/'); |
||||
$kernel->parseDotEnv($kernel->getProjectDir() . '/.env'); |
||||
require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; |
||||
|
||||
ini_set('date.timezone', $_ENV['TZ']); |
||||
|
||||
$kernel->handle()->send(); |
||||
return function (array $context) { |
||||
return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); |
||||
}; |
||||
|
@ -1,101 +1,86 @@ |
||||
<?php |
||||
|
||||
|
||||
namespace JeroenED\Webcron\Controller; |
||||
namespace App\Controller; |
||||
|
||||
use JeroenED\Framework\Controller; |
||||
use JeroenED\Framework\Repository; |
||||
use JeroenED\Webcron\Repository\Job; |
||||
use JeroenED\Webcron\Repository\Run; |
||||
use App\Entity\Job; |
||||
use App\Entity\Run; |
||||
use Doctrine\Persistence\ManagerRegistry; |
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; |
||||
use Symfony\Component\HttpFoundation\JsonResponse; |
||||
use Symfony\Component\HttpFoundation\RedirectResponse; |
||||
use Symfony\Component\HttpFoundation\Request; |
||||
use Symfony\Component\HttpFoundation\Response; |
||||
|
||||
class JobController extends Controller |
||||
class JobController extends AbstractController |
||||
{ |
||||
public function DefaultAction() |
||||
public function defaultAction(ManagerRegistry $doctrine): Response |
||||
{ |
||||
if(!isset($_SESSION['isAuthenticated']) || !$_SESSION['isAuthenticated']) { |
||||
return new RedirectResponse($this->generateRoute('login')); |
||||
} |
||||
$jobRepo = new Job($this->getDbCon()); |
||||
$jobRepo = $doctrine->getRepository(Job::class); |
||||
$jobs = $jobRepo->getAllJobs(); |
||||
return $this->render('job/index.html.twig', ['jobs' => $jobs]); |
||||
} |
||||
|
||||
public function jobAction($id, $all = false) |
||||
public function jobAction(Request $request, ManagerRegistry $doctrine, $id, $all = false): Response |
||||
{ |
||||
if(!isset($_SESSION['isAuthenticated']) || !$_SESSION['isAuthenticated']) { |
||||
return new RedirectResponse($this->generateRoute('login')); |
||||
} |
||||
$jobRepo = new Job($this->getDbCon()); |
||||
$runRepo = new Run($this->getDbCon()); |
||||
$jobRepo = $doctrine->getRepository(Job::class); |
||||
$runRepo = $doctrine->getRepository(Run::class); |
||||
|
||||
if($this->getRequest()->getMethod() == 'GET') { |
||||
if($request->getMethod() == 'GET') { |
||||
$job = $jobRepo->getJob($id); |
||||
$runs = $runRepo->getRunsForJob($id, $all != 'all'); |
||||
return $this->render('job/view.html.twig', ['job' => $job, 'runs' => $runs, 'allruns' => $all == 'all']); |
||||
} elseif($this->getRequest()->getMethod() == 'DELETE') { |
||||
} elseif($request->getMethod() == 'DELETE') { |
||||
$success = $jobRepo->deleteJob($id); |
||||
$this->addFlash('success', $success['message']); |
||||
return new JsonResponse(['return_path' => $this->generateRoute('job_index')]); |
||||
return new JsonResponse(['return_path' => $this->GenerateUrl('job_index')]); |
||||
} |
||||
} |
||||
|
||||
public function editAction($id) |
||||
public function editAction(Request $request, ManagerRegistry $doctrine, $id) |
||||
{ |
||||
if(!isset($_SESSION['isAuthenticated']) || !$_SESSION['isAuthenticated']) { |
||||
return new RedirectResponse($this->generateRoute('login')); |
||||
} |
||||
if($this->getRequest()->getMethod() == 'GET') { |
||||
$jobRepo = new Job($this->getDbCon()); |
||||
if($request->getMethod() == 'GET') { |
||||
$jobRepo = $doctrine->getRepository(Job::class); |
||||
$job = $jobRepo->getJob($id, true); |
||||
return $this->render('job/edit.html.twig', $job); |
||||
} elseif($this->getRequest()->getMethod() == 'POST') { |
||||
$allValues = $this->getRequest()->request->all(); |
||||
$jobRepo = new Job($this->getDbCon()); |
||||
} elseif($request->getMethod() == 'POST') { |
||||
$allValues = $request->request->all(); |
||||
$jobRepo = $doctrine->getRepository(Job::class); |
||||
|
||||
try { |
||||
$joboutput = $jobRepo->editJob($id, $allValues); |
||||
} catch (\InvalidArgumentException $e) { |
||||
$this->addFlash('danger', $e->getMessage()); |
||||
return new RedirectResponse($this->generateRoute('job_edit', ['id' => $allValues['id']])); |
||||
return new RedirectResponse($this->GenerateUrl('job_edit', ['id' => $allValues['id']])); |
||||
} |
||||
$this->addFlash('success', $joboutput['message']); |
||||
return new RedirectResponse($this->generateRoute('job_index')); |
||||
return new RedirectResponse($this->GenerateUrl('job_index')); |
||||
} |
||||
} |
||||
|
||||
public function addAction() |
||||
public function addAction(Request $request, ManagerRegistry $doctrine) |
||||
{ |
||||
if(!isset($_SESSION['isAuthenticated']) || !$_SESSION['isAuthenticated']) { |
||||
return new RedirectResponse($this->generateRoute('login')); |
||||
} |
||||
|
||||
if($this->getRequest()->getMethod() == 'GET') { |
||||
return $this->render('job/add.html.twig'); |
||||
} elseif ($this->getRequest()->getMethod() == 'POST') { |
||||
$allValues = $this->getRequest()->request->all(); |
||||
$jobRepo = new Job($this->getDbCon()); |
||||
if($request->getMethod() == 'GET') { |
||||
return $this->render('job/add.html.twig', ['data' => []]); |
||||
} elseif ($request->getMethod() == 'POST') { |
||||
$allValues = $request->request->all(); |
||||
$jobRepo = $doctrine->getRepository(Job::class); |
||||
try { |
||||
$joboutput = $jobRepo->addJob($allValues); |
||||
} catch (\InvalidArgumentException $e) { |
||||