Initial commit

This commit is contained in:
Skylar Sadlier 2023-09-09 03:29:51 -06:00
parent 0cce30679a
commit e8df5bf019
20 changed files with 1132 additions and 10 deletions

10
.env
View File

@ -25,8 +25,8 @@ APP_SECRET=0ac2ef94aa7fbf17c0fa7caca5d59ba8
# #
# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db" # DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8.0.32&charset=utf8mb4" # DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8.0.32&charset=utf8mb4"
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4" # DATABASE_URL="mysql://MyComments:Plentiful-Easiness-Gracious6-Boggle@127.0.0.1:3306/MyComments?serverVersion=11.1.2-MariaDB&charset=utf8mb4"
DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=15&charset=utf8" DATABASE_URL="postgresql://app:Liqueur-Battalion7-Woof-Spokesman@127.0.0.1:5432/app?serverVersion=15&charset=utf8"
###< doctrine/doctrine-bundle ### ###< doctrine/doctrine-bundle ###
###> symfony/messenger ### ###> symfony/messenger ###
@ -39,3 +39,9 @@ MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0
###> symfony/mailer ### ###> symfony/mailer ###
# MAILER_DSN=null://null # MAILER_DSN=null://null
###< symfony/mailer ### ###< symfony/mailer ###
###> symfony/lock ###
# Choose one of the stores below
# postgresql+advisory://db_user:db_password@localhost/db_name
LOCK_DSN=flock
###< symfony/lock ###

3
.gitignore vendored
View File

@ -18,3 +18,6 @@
.phpunit.result.cache .phpunit.result.cache
/phpunit.xml /phpunit.xml
###< symfony/phpunit-bridge ### ###< symfony/phpunit-bridge ###
# IDE folders
.idea

20
LICENSE
View File

@ -1,9 +1,21 @@
MIT License MIT License
Copyright (c) 2024 skylord123 Copyright (c) 2023 MyComments
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -10,6 +10,7 @@
"doctrine/doctrine-bundle": "^2.10", "doctrine/doctrine-bundle": "^2.10",
"doctrine/doctrine-migrations-bundle": "^3.2", "doctrine/doctrine-migrations-bundle": "^3.2",
"doctrine/orm": "^2.16", "doctrine/orm": "^2.16",
"gedmo/doctrine-extensions": "^3.13",
"phpdocumentor/reflection-docblock": "^5.3", "phpdocumentor/reflection-docblock": "^5.3",
"phpstan/phpdoc-parser": "^1.24", "phpstan/phpdoc-parser": "^1.24",
"symfony/asset": "6.3.*", "symfony/asset": "6.3.*",
@ -22,6 +23,7 @@
"symfony/framework-bundle": "6.3.*", "symfony/framework-bundle": "6.3.*",
"symfony/http-client": "6.3.*", "symfony/http-client": "6.3.*",
"symfony/intl": "6.3.*", "symfony/intl": "6.3.*",
"symfony/lock": "6.3.*",
"symfony/mailer": "6.3.*", "symfony/mailer": "6.3.*",
"symfony/mime": "6.3.*", "symfony/mime": "6.3.*",
"symfony/monolog-bundle": "^3.0", "symfony/monolog-bundle": "^3.0",
@ -29,6 +31,7 @@
"symfony/process": "6.3.*", "symfony/process": "6.3.*",
"symfony/property-access": "6.3.*", "symfony/property-access": "6.3.*",
"symfony/property-info": "6.3.*", "symfony/property-info": "6.3.*",
"symfony/rate-limiter": "6.3.*",
"symfony/runtime": "6.3.*", "symfony/runtime": "6.3.*",
"symfony/security-bundle": "6.3.*", "symfony/security-bundle": "6.3.*",
"symfony/serializer": "6.3.*", "symfony/serializer": "6.3.*",

403
composer.lock generated
View File

@ -4,8 +4,133 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "e077926b0d83c217ece524f4a3d61fa8", "content-hash": "0e74667a94096c60a3ca270a79c1331d",
"packages": [ "packages": [
{
"name": "behat/transliterator",
"version": "v1.5.0",
"source": {
"type": "git",
"url": "https://github.com/Behat/Transliterator.git",
"reference": "baac5873bac3749887d28ab68e2f74db3a4408af"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Behat/Transliterator/zipball/baac5873bac3749887d28ab68e2f74db3a4408af",
"reference": "baac5873bac3749887d28ab68e2f74db3a4408af",
"shasum": ""
},
"require": {
"php": ">=7.2"
},
"require-dev": {
"chuyskywalker/rolling-curl": "^3.1",
"php-yaoi/php-yaoi": "^1.0",
"phpunit/phpunit": "^8.5.25 || ^9.5.19"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"Behat\\Transliterator\\": "src/Behat/Transliterator"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Artistic-1.0"
],
"description": "String transliterator",
"keywords": [
"i18n",
"slug",
"transliterator"
],
"support": {
"issues": "https://github.com/Behat/Transliterator/issues",
"source": "https://github.com/Behat/Transliterator/tree/v1.5.0"
},
"time": "2022-03-30T09:27:43+00:00"
},
{
"name": "doctrine/annotations",
"version": "2.0.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/annotations.git",
"reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f",
"reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f",
"shasum": ""
},
"require": {
"doctrine/lexer": "^2 || ^3",
"ext-tokenizer": "*",
"php": "^7.2 || ^8.0",
"psr/cache": "^1 || ^2 || ^3"
},
"require-dev": {
"doctrine/cache": "^2.0",
"doctrine/coding-standard": "^10",
"phpstan/phpstan": "^1.8.0",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"symfony/cache": "^5.4 || ^6",
"vimeo/psalm": "^4.10"
},
"suggest": {
"php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations"
},
"type": "library",
"autoload": {
"psr-4": {
"Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
},
{
"name": "Benjamin Eberlei",
"email": "kontakt@beberlei.de"
},
{
"name": "Jonathan Wage",
"email": "jonwage@gmail.com"
},
{
"name": "Johannes Schmitt",
"email": "schmittjoh@gmail.com"
}
],
"description": "Docblock Annotations Parser",
"homepage": "https://www.doctrine-project.org/projects/annotations.html",
"keywords": [
"annotations",
"docblock",
"parser"
],
"support": {
"issues": "https://github.com/doctrine/annotations/issues",
"source": "https://github.com/doctrine/annotations/tree/2.0.1"
},
"time": "2023-02-02T22:02:53+00:00"
},
{ {
"name": "doctrine/cache", "name": "doctrine/cache",
"version": "2.2.0", "version": "2.2.0",
@ -1387,6 +1512,133 @@
], ],
"time": "2023-01-14T14:17:03+00:00" "time": "2023-01-14T14:17:03+00:00"
}, },
{
"name": "gedmo/doctrine-extensions",
"version": "v3.13.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine-extensions/DoctrineExtensions.git",
"reference": "291d0c527d2dc9ee07b888c9a4e2a179893f08ab"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine-extensions/DoctrineExtensions/zipball/291d0c527d2dc9ee07b888c9a4e2a179893f08ab",
"reference": "291d0c527d2dc9ee07b888c9a4e2a179893f08ab",
"shasum": ""
},
"require": {
"behat/transliterator": "^1.2",
"doctrine/annotations": "^1.13 || ^2.0",
"doctrine/collections": "^1.2 || ^2.0",
"doctrine/common": "^2.13 || ^3.0",
"doctrine/event-manager": "^1.2 || ^2.0",
"doctrine/persistence": "^2.2 || ^3.0",
"php": "^7.2 || ^8.0",
"psr/cache": "^1 || ^2 || ^3",
"symfony/cache": "^4.4 || ^5.3 || ^6.0",
"symfony/deprecation-contracts": "^2.1 || ^3.0"
},
"conflict": {
"doctrine/dbal": "<2.13.1 || ^3.0 <3.2",
"doctrine/mongodb-odm": "<2.3",
"doctrine/orm": "<2.10.2 || 2.16.0 || 2.16.1",
"sebastian/comparator": "<2.0"
},
"require-dev": {
"doctrine/cache": "^1.11 || ^2.0",
"doctrine/dbal": "^2.13.1 || ^3.2",
"doctrine/doctrine-bundle": "^2.3",
"doctrine/mongodb-odm": "^2.3",
"doctrine/orm": "^2.10.2",
"friendsofphp/php-cs-fixer": "^3.4.0 <3.10",
"nesbot/carbon": "^2.55",
"phpstan/phpstan": "^1.10.2",
"phpstan/phpstan-doctrine": "^1.0",
"phpstan/phpstan-phpunit": "^1.0",
"phpunit/phpunit": "^8.5 || ^9.5",
"rector/rector": "^0.15.20",
"symfony/console": "^4.4 || ^5.3 || ^6.0",
"symfony/phpunit-bridge": "^6.0",
"symfony/yaml": "^4.4 || ^5.3 || ^6.0"
},
"suggest": {
"doctrine/mongodb-odm": "to use the extensions with the MongoDB ODM",
"doctrine/orm": "to use the extensions with the ORM"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.13-dev"
}
},
"autoload": {
"psr-4": {
"Gedmo\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Gediminas Morkevicius",
"email": "gediminas.morkevicius@gmail.com"
},
{
"name": "Gustavo Falco",
"email": "comfortablynumb84@gmail.com"
},
{
"name": "David Buchmann",
"email": "david@liip.ch"
}
],
"description": "Doctrine behavioral extensions",
"homepage": "http://gediminasm.org/",
"keywords": [
"Blameable",
"behaviors",
"doctrine",
"extensions",
"gedmo",
"loggable",
"nestedset",
"odm",
"orm",
"sluggable",
"sortable",
"timestampable",
"translatable",
"tree",
"uploadable"
],
"support": {
"email": "gediminas.morkevicius@gmail.com",
"issues": "https://github.com/doctrine-extensions/DoctrineExtensions/issues",
"source": "https://github.com/doctrine-extensions/DoctrineExtensions/tree/v3.13.0",
"wiki": "https://github.com/Atlantic18/DoctrineExtensions/tree/main/doc"
},
"funding": [
{
"url": "https://github.com/l3pp4rd",
"type": "github"
},
{
"url": "https://github.com/mbabker",
"type": "github"
},
{
"url": "https://github.com/phansys",
"type": "github"
},
{
"url": "https://github.com/stof",
"type": "github"
}
],
"time": "2023-09-06T13:16:12+00:00"
},
{ {
"name": "monolog/monolog", "name": "monolog/monolog",
"version": "3.4.0", "version": "3.4.0",
@ -4061,6 +4313,85 @@
], ],
"time": "2023-07-20T07:43:09+00:00" "time": "2023-07-20T07:43:09+00:00"
}, },
{
"name": "symfony/lock",
"version": "v6.3.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/lock.git",
"reference": "cde6dbd72d217024b1049d42a4519e713e2f18af"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/lock/zipball/cde6dbd72d217024b1049d42a4519e713e2f18af",
"reference": "cde6dbd72d217024b1049d42a4519e713e2f18af",
"shasum": ""
},
"require": {
"php": ">=8.1",
"psr/log": "^1|^2|^3",
"symfony/deprecation-contracts": "^2.5|^3"
},
"conflict": {
"doctrine/dbal": "<2.13",
"symfony/cache": "<6.2"
},
"require-dev": {
"doctrine/dbal": "^2.13|^3.0",
"predis/predis": "^1.1|^2.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Lock\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jérémy Derussé",
"email": "jeremy@derusse.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Creates and manages locks, a mechanism to provide exclusive access to a shared resource",
"homepage": "https://symfony.com",
"keywords": [
"cas",
"flock",
"locking",
"mutex",
"redlock",
"semaphore"
],
"support": {
"source": "https://github.com/symfony/lock/tree/v6.3.2"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2023-06-28T15:25:15+00:00"
},
{ {
"name": "symfony/mailer", "name": "symfony/mailer",
"version": "v6.3.0", "version": "v6.3.0",
@ -5410,6 +5741,76 @@
], ],
"time": "2023-05-19T08:06:44+00:00" "time": "2023-05-19T08:06:44+00:00"
}, },
{
"name": "symfony/rate-limiter",
"version": "v6.3.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/rate-limiter.git",
"reference": "5223387e4017f26c8c61c46d8e39c11fe4d504b7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/rate-limiter/zipball/5223387e4017f26c8c61c46d8e39c11fe4d504b7",
"reference": "5223387e4017f26c8c61c46d8e39c11fe4d504b7",
"shasum": ""
},
"require": {
"php": ">=8.1",
"symfony/options-resolver": "^5.4|^6.0"
},
"require-dev": {
"psr/cache": "^1.0|^2.0|^3.0",
"symfony/lock": "^5.4|^6.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\RateLimiter\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Wouter de Jong",
"email": "wouter@wouterj.nl"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Provides a Token Bucket implementation to rate limit input and output in your application",
"homepage": "https://symfony.com",
"keywords": [
"limiter",
"rate-limiter"
],
"support": {
"source": "https://github.com/symfony/rate-limiter/tree/v6.3.2"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2023-07-13T14:29:38+00:00"
},
{ {
"name": "symfony/routing", "name": "symfony/routing",
"version": "v6.3.3", "version": "v6.3.3",

View File

@ -0,0 +1,2 @@
framework:
lock: '%env(LOCK_DSN)%'

View File

@ -1,17 +1,57 @@
framework:
rate_limiter:
# define 2 rate limiters (one for username+IP, the other for IP)
username_ip_login:
policy: token_bucket
limit: 5
rate: { interval: '5 minutes' }
ip_login:
policy: sliding_window
limit: 40
interval: '30 minutes'
services:
# our custom login rate limiter
app.login_rate_limiter:
class: Symfony\Component\Security\Http\RateLimiter\DefaultLoginRateLimiter
arguments:
# globalFactory is the limiter for IP
$globalFactory: '@limiter.ip_login'
# localFactory is the limiter for username+IP
$localFactory: '@limiter.username_ip_login'
security: security:
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
password_hashers: password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
providers: providers:
users_in_memory: { memory: null } # used to reload user from session & other features (e.g. switch_user)
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls: firewalls:
dev: dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/ pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false security: false
main: main:
lazy: true lazy: true
provider: users_in_memory provider: app_user_provider
form_login:
login_path: app_login
check_path: app_login
#logout:
# path: app_logout
# where to redirect after logout
# target: app_any_route
# configure the maximum login attempts
login_throttling:
limiter: app.login_rate_limiter
# activate different ways to authenticate # activate different ways to authenticate
# https://symfony.com/doc/current/security.html#the-firewall # https://symfony.com/doc/current/security.html#the-firewall

View File

@ -7,7 +7,7 @@ services:
environment: environment:
POSTGRES_DB: ${POSTGRES_DB:-app} POSTGRES_DB: ${POSTGRES_DB:-app}
# You should definitely change the password in production # You should definitely change the password in production
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-!ChangeMe!} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-Liqueur-Battalion7-Woof-Spokesman}
POSTGRES_USER: ${POSTGRES_USER:-app} POSTGRES_USER: ${POSTGRES_USER:-app}
volumes: volumes:
- database_data:/var/lib/postgresql/data:rw - database_data:/var/lib/postgresql/data:rw

View File

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20230909083131 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE domain (id INT AUTO_INCREMENT NOT NULL, domain VARCHAR(255) NOT NULL, owner_token VARCHAR(64) NOT NULL, name VARCHAR(255) NOT NULL, enabled TINYINT(1) NOT NULL, default_sort_policy VARCHAR(32) NOT NULL, updated_at DATETIME DEFAULT NULL, created_at DATETIME NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('CREATE TABLE email (id INT AUTO_INCREMENT NOT NULL, email VARCHAR(255) NOT NULL, unsubscribe_token VARCHAR(255) NOT NULL, last_notification_sent_at DATETIME DEFAULT NULL, pending_emails INT NOT NULL, send_reply_notifications TINYINT(1) NOT NULL, send_moderator_notifications TINYINT(1) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('CREATE TABLE `user` (id INT AUTO_INCREMENT NOT NULL, email VARCHAR(180) NOT NULL, roles LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\', password VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_8D93D649E7927C74 (email), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('CREATE TABLE messenger_messages (id BIGINT AUTO_INCREMENT NOT NULL, body LONGTEXT NOT NULL, headers LONGTEXT NOT NULL, queue_name VARCHAR(190) NOT NULL, created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', available_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', delivered_at DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', INDEX IDX_75EA56E0FB7336F0 (queue_name), INDEX IDX_75EA56E0E3BD61CE (available_at), INDEX IDX_75EA56E016BA31DB (delivered_at), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP TABLE domain');
$this->addSql('DROP TABLE email');
$this->addSql('DROP TABLE `user`');
$this->addSql('DROP TABLE messenger_messages');
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('IS_AUTHENTICATED_FULLY')]
class DashboardController extends AbstractController
{
#[Route('/', name: 'app_dashboard')]
public function index(): Response
{
return $this->render('dashboard/index.html.twig', [
'controller_name' => 'DashboardController',
]);
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class SecurityController extends AbstractController
{
#[Route('/login', name: 'app_login')]
public function login(AuthenticationUtils $authenticationUtils): Response
{
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('login/index.html.twig', [
'controller_name' => 'LoginController',
'last_username' => $lastUsername,
'error' => $error,
]);
}
#[Route('/logout', name: 'app_logout', methods: ['GET'])]
public function logout(Security $security): Response
{
return $security->logout(false);
}
}

129
src/Entity/Domain.php Normal file
View File

@ -0,0 +1,129 @@
<?php
namespace App\Entity;
use App\Repository\DomainRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation\Timestampable;
#[ORM\Entity(repositoryClass: DomainRepository::class)]
class Domain
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $domain = null;
#[ORM\Column(length: 64)]
private ?string $ownerToken = null;
#[ORM\Column(length: 255)]
private ?string $name = null;
#[ORM\Column]
private ?bool $enabled = null;
#[ORM\Column(length: 32)]
private ?string $defaultSortPolicy = null;
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
#[Timestampable(on: "update")]
private ?\DateTimeInterface $updatedAt = null;
#[ORM\Column(type: Types::DATETIME_MUTABLE)]
#[Timestampable(on: "create")]
private ?\DateTimeInterface $createdAt = null;
public function getId(): ?int
{
return $this->id;
}
public function getDomain(): ?string
{
return $this->domain;
}
public function setDomain(string $domain): static
{
$this->domain = $domain;
return $this;
}
public function getOwnerToken(): ?string
{
return $this->ownerToken;
}
public function setOwnerToken(string $ownerToken): static
{
$this->ownerToken = $ownerToken;
return $this;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): static
{
$this->name = $name;
return $this;
}
public function isEnabled(): ?bool
{
return $this->enabled;
}
public function setEnabled(bool $enabled): static
{
$this->enabled = $enabled;
return $this;
}
public function getDefaultSortPolicy(): ?string
{
return $this->defaultSortPolicy;
}
public function setDefaultSortPolicy(string $defaultSortPolicy): static
{
$this->defaultSortPolicy = $defaultSortPolicy;
return $this;
}
public function getUpdatedAt(): ?\DateTimeInterface
{
return $this->updatedAt;
}
public function setUpdatedAt(?\DateTimeInterface $updatedAt): static
{
$this->updatedAt = $updatedAt;
return $this;
}
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeInterface $createdAt): static
{
$this->createdAt = $createdAt;
return $this;
}
}

111
src/Entity/Email.php Normal file
View File

@ -0,0 +1,111 @@
<?php
namespace App\Entity;
use App\Repository\EmailRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: EmailRepository::class)]
class Email
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $email = null;
#[ORM\Column(length: 255)]
private ?string $unsubscribeToken = null;
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
private ?\DateTimeInterface $lastNotificationSentAt = null;
#[ORM\Column]
private ?int $pendingEmails = null;
#[ORM\Column]
private ?bool $sendReplyNotifications = null;
#[ORM\Column]
private ?bool $sendModeratorNotifications = null;
public function getId(): ?int
{
return $this->id;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): static
{
$this->email = $email;
return $this;
}
public function getUnsubscribeToken(): ?string
{
return $this->unsubscribeToken;
}
public function setUnsubscribeToken(string $unsubscribeToken): static
{
$this->unsubscribeToken = $unsubscribeToken;
return $this;
}
public function getLastNotificationSentAt(): ?\DateTimeInterface
{
return $this->lastNotificationSentAt;
}
public function setLastNotificationSentAt(?\DateTimeInterface $lastNotificationSentAt): static
{
$this->lastNotificationSentAt = $lastNotificationSentAt;
return $this;
}
public function getPendingEmails(): ?int
{
return $this->pendingEmails;
}
public function setPendingEmails(int $pendingEmails): static
{
$this->pendingEmails = $pendingEmails;
return $this;
}
public function isSendReplyNotifications(): ?bool
{
return $this->sendReplyNotifications;
}
public function setSendReplyNotifications(bool $sendReplyNotifications): static
{
$this->sendReplyNotifications = $sendReplyNotifications;
return $this;
}
public function isSendModeratorNotifications(): ?bool
{
return $this->sendModeratorNotifications;
}
public function setSendModeratorNotifications(bool $sendModeratorNotifications): static
{
$this->sendModeratorNotifications = $sendModeratorNotifications;
return $this;
}
}

100
src/Entity/User.php Normal file
View File

@ -0,0 +1,100 @@
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
#[ORM\Entity(repositoryClass: UserRepository::class)]
#[ORM\Table(name: '`user`')]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 180, unique: true)]
private ?string $email = null;
#[ORM\Column]
private array $roles = [];
/**
* @var string The hashed password
*/
#[ORM\Column]
private ?string $password = null;
public function getId(): ?int
{
return $this->id;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): static
{
$this->email = $email;
return $this;
}
/**
* A visual identifier that represents this user.
*
* @see UserInterface
*/
public function getUserIdentifier(): string
{
return (string) $this->email;
}
/**
* @see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): static
{
$this->roles = $roles;
return $this;
}
/**
* @see PasswordAuthenticatedUserInterface
*/
public function getPassword(): string
{
return $this->password;
}
public function setPassword(string $password): static
{
$this->password = $password;
return $this;
}
/**
* @see UserInterface
*/
public function eraseCredentials(): void
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
}

View File

@ -0,0 +1,48 @@
<?php
namespace App\Repository;
use App\Entity\Domain;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<Domain>
*
* @method Domain|null find($id, $lockMode = null, $lockVersion = null)
* @method Domain|null findOneBy(array $criteria, array $orderBy = null)
* @method Domain[] findAll()
* @method Domain[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class DomainRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Domain::class);
}
// /**
// * @return Domain[] Returns an array of Domain objects
// */
// public function findByExampleField($value): array
// {
// return $this->createQueryBuilder('d')
// ->andWhere('d.exampleField = :val')
// ->setParameter('val', $value)
// ->orderBy('d.id', 'ASC')
// ->setMaxResults(10)
// ->getQuery()
// ->getResult()
// ;
// }
// public function findOneBySomeField($value): ?Domain
// {
// return $this->createQueryBuilder('d')
// ->andWhere('d.exampleField = :val')
// ->setParameter('val', $value)
// ->getQuery()
// ->getOneOrNullResult()
// ;
// }
}

View File

@ -0,0 +1,48 @@
<?php
namespace App\Repository;
use App\Entity\Email;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<Email>
*
* @method Email|null find($id, $lockMode = null, $lockVersion = null)
* @method Email|null findOneBy(array $criteria, array $orderBy = null)
* @method Email[] findAll()
* @method Email[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class EmailRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Email::class);
}
// /**
// * @return Email[] Returns an array of Email objects
// */
// public function findByExampleField($value): array
// {
// return $this->createQueryBuilder('e')
// ->andWhere('e.exampleField = :val')
// ->setParameter('val', $value)
// ->orderBy('e.id', 'ASC')
// ->setMaxResults(10)
// ->getQuery()
// ->getResult()
// ;
// }
// public function findOneBySomeField($value): ?Email
// {
// return $this->createQueryBuilder('e')
// ->andWhere('e.exampleField = :val')
// ->setParameter('val', $value)
// ->getQuery()
// ->getOneOrNullResult()
// ;
// }
}

View File

@ -0,0 +1,67 @@
<?php
namespace App\Repository;
use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
/**
* @extends ServiceEntityRepository<User>
*
* @implements PasswordUpgraderInterface<User>
*
* @method User|null find($id, $lockMode = null, $lockVersion = null)
* @method User|null findOneBy(array $criteria, array $orderBy = null)
* @method User[] findAll()
* @method User[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class UserRepository extends ServiceEntityRepository implements PasswordUpgraderInterface
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, User::class);
}
/**
* Used to upgrade (rehash) the user's password automatically over time.
*/
public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void
{
if (!$user instanceof User) {
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', $user::class));
}
$user->setPassword($newHashedPassword);
$this->getEntityManager()->persist($user);
$this->getEntityManager()->flush();
}
// /**
// * @return User[] Returns an array of User objects
// */
// public function findByExampleField($value): array
// {
// return $this->createQueryBuilder('u')
// ->andWhere('u.exampleField = :val')
// ->setParameter('val', $value)
// ->orderBy('u.id', 'ASC')
// ->setMaxResults(10)
// ->getQuery()
// ->getResult()
// ;
// }
// public function findOneBySomeField($value): ?User
// {
// return $this->createQueryBuilder('u')
// ->andWhere('u.exampleField = :val')
// ->setParameter('val', $value)
// ->getQuery()
// ->getOneOrNullResult()
// ;
// }
}

View File

@ -1,4 +1,13 @@
{ {
"doctrine/annotations": {
"version": "2.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "1.10",
"ref": "64d8583af5ea57b7afa4aba4b159907f3a148b05"
}
},
"doctrine/doctrine-bundle": { "doctrine/doctrine-bundle": {
"version": "2.10", "version": "2.10",
"recipe": { "recipe": {
@ -95,6 +104,18 @@
"src/Kernel.php" "src/Kernel.php"
] ]
}, },
"symfony/lock": {
"version": "6.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "5.2",
"ref": "8e937ff2b4735d110af1770f242c1107fdab4c8e"
},
"files": [
"config/packages/lock.yaml"
]
},
"symfony/mailer": { "symfony/mailer": {
"version": "6.3", "version": "6.3",
"recipe": { "recipe": {

View File

@ -0,0 +1,20 @@
{% extends 'base.html.twig' %}
{% block title %}Hello DashboardController!{% endblock %}
{% block body %}
<style>
.example-wrapper { margin: 1em auto; max-width: 800px; width: 95%; font: 18px/1.5 sans-serif; }
.example-wrapper code { background: #F5F5F5; padding: 2px 6px; }
</style>
<div class="example-wrapper">
<h1>Hello {{ controller_name }}! ✅</h1>
This friendly message is coming from:
<ul>
<li>Your controller at <code><a href="{{ '/home/skylar/Projects/MyComments/src/Controller/DashboardController.php'|file_link(0) }}">src/Controller/DashboardController.php</a></code></li>
<li>Your template at <code><a href="{{ '/home/skylar/Projects/MyComments/templates/dashboard/index.html.twig'|file_link(0) }}">templates/dashboard/index.html.twig</a></code></li>
</ul>
</div>
{% endblock %}

View File

@ -0,0 +1,20 @@
{% extends 'base.html.twig' %}
{% block body %}
{% if error %}
<div>{{ error.messageKey|trans(error.messageData, 'security') }}</div>
{% endif %}
<form action="{{ path('app_login') }}" method="post">
<label for="username">Email:</label>
<input type="text" id="username" name="_username" value="{{ last_username }}">
<label for="password">Password:</label>
<input type="password" id="password" name="_password">
{# If you want to control the URL the user is redirected to on success
<input type="hidden" name="_target_path" value="/account"> #}
<button type="submit">login</button>
</form>
{% endblock %}