Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions resources/config/.gitignore.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Encryption keys - NEVER commit these
/master.key
/secrets/*.key
*.key

# Local environment overrides
.env
.env.local
.env.*.local

# Environment-specific configuration (if not using encrypted secrets)
/environments/*.local.yaml

# Temporary files
*.tmp
*.bak
*~

# IDE settings (optional, uncomment if needed)
#.idea/
#.vscode/
#*.swp
#*.swo
1 change: 1 addition & 0 deletions resources/config/neuron.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ theme:

# Security Configuration
security:
enabled: true # Dummy value to prevent null errors in PHP 8.4
# Content Security Policy (CSP)
# Customize this based on your application's needs
# content_security_policy: "default-src 'self'; script-src 'self' 'unsafe-inline';"
Expand Down
27 changes: 27 additions & 0 deletions resources/config/neuron.yaml.example
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,27 @@ cloudinary:
# All three databases (SQLite, MySQL, PostgreSQL) are fully supported
# with identical behavior - choose based on your deployment needs

# === NEW: URL-Based Configuration ===
# You can now configure your database using a single URL string
# This is especially useful for cloud platforms and environment-based configs
#
# Format: adapter://user:pass@host:port/database?options
#
# Examples:
# database:
# url: mysql://myuser:mypass@localhost:3306/neuron_cms?charset=utf8mb4
# url: postgresql://postgres:secret@db.example.com:5432/myapp
# url: sqlite:///storage/database.sqlite3
# url: sqlite::memory: # For in-memory database
#
# Environment variable (takes precedence over config file):
# DATABASE_URL=mysql://user:pass@host:3306/dbname
#
# You can also mix URL with individual overrides:
# database:
# url: mysql://user:pass@host:3306/dbname
# charset: latin1 # Override the charset from URL

# Option 1: SQLite (Recommended for development and small-to-medium sites)
# - Zero configuration required
# - Single file database
Expand All @@ -106,6 +127,8 @@ cloudinary:
database:
adapter: sqlite
name: storage/database.sqlite3
# Or use URL format:
# url: sqlite:///storage/database.sqlite3

# Option 2: MySQL (Recommended for large-scale production)
# - Best for high-traffic websites
Expand All @@ -119,6 +142,8 @@ database:
# user: root
# pass: secret
# charset: utf8mb4 # UTF8MB4 recommended for full Unicode support
# # Or use URL format:
# # url: mysql://root:secret@localhost:3306/neuron_cms?charset=utf8mb4

# Option 3: PostgreSQL (Enterprise features and complex queries)
# - Advanced query capabilities
Expand All @@ -131,6 +156,8 @@ database:
# name: neuron_cms
# user: postgres
# pass: secret
# # Or use URL format:
# # url: postgresql://postgres:secret@localhost:5432/neuron_cms

# Note: All databases use UTC timezone and have foreign key constraints enforced
# Timestamp updates (created_at, updated_at) work identically across all platforms
Expand Down
2 changes: 1 addition & 1 deletion resources/views/auth/password_reset/forgot-password.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<body>
<h1>Forgot Password</h1>
<p>Enter your email address and we'll send you a password reset link.</p>
<form method="POST" action="/auth/password-reset/send">
<form method="POST" action="/forgot-password">
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrfToken ?? '') ?>">

<label>Email:</label>
Expand Down
74 changes: 56 additions & 18 deletions src/Bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
use Neuron\Data\Objects\Version;
use Neuron\Data\Settings\Source\Yaml;
use Neuron\Data\Settings\SettingManager;
use Neuron\Data\Settings\SettingManagerFactory;
use Neuron\Data\Factories;
use Neuron\Mvc\Application;
use Neuron\Mvc\IMvcApplication;
use Neuron\Orm\Model;
Expand Down Expand Up @@ -42,41 +44,77 @@

function boot( string $configPath ) : Application
{
// Initialize settings variable to avoid undefined variable error
$settings = null;
$container = null;

// Build and register the DI container BEFORE MVC boot
// This ensures the container is available to initializers during boot
try
{
// Determine which configuration file to load based on environment
// Determine environment
// Check APP_ENV environment variable (testing, development, production)
$environment = getenv( 'APP_ENV' ) ?: 'production';
$configFile = match( $environment ) {
'testing' => 'neuron.testing.yaml',
'development' => 'neuron.yaml',
'production' => 'neuron.yaml',
default => 'neuron.yaml'
};

// Fallback to neuron.yaml if environment-specific file doesn't exist
$configFilePath = "$configPath/$configFile";
if( !file_exists( $configFilePath ) )
{
$configFilePath = "$configPath/neuron.yaml";
}
// Use SettingManagerFactory for comprehensive configuration loading
// This will automatically load:
// 1. neuron.yaml (base configuration)
// 2. Environment-specific config if exists
// 3. Encrypted secrets from config/secrets.yml.enc (if exists)
// 4. Environment-specific encrypted secrets if exist
// 5. Environment variables (highest priority)
$sources = [
[
'type' => 'yaml',
'path' => match( $environment ) {
'testing' => file_exists( "$configPath/neuron.testing.yaml" )
? "$configPath/neuron.testing.yaml"
: "$configPath/neuron.yaml",
default => "$configPath/neuron.yaml"
},
'name' => 'config'
],
// Only include main secrets if both the encrypted file and key exist
file_exists( "$configPath/secrets.yml.enc" ) && file_exists( "$configPath/master.key" ) ? [
'type' => 'encrypted',
'path' => "$configPath/secrets.yml.enc",
'key' => "$configPath/master.key",
'name' => 'secrets'
] : null,
// Only include environment-specific secrets if both files exist
file_exists( "$configPath/secrets/$environment.yml.enc" ) && file_exists( "$configPath/secrets/$environment.key" ) ? [
'type' => 'encrypted',
'path' => "$configPath/secrets/$environment.yml.enc",
'key' => "$configPath/secrets/$environment.key",
'name' => "secrets:$environment"
] : null,
[
'type' => 'env',
'name' => 'environment'
]
];

// Create SettingManager from config file
$yaml = new Yaml( $configFilePath );
$settings = new SettingManager( $yaml );
// Filter out null entries (non-existent encrypted sources)
$settings = SettingManagerFactory::createCustom( array_filter( $sources ) );

// Build container (automatically registers in Registry)
$container = Container::build( $settings );
}
catch( \Exception $e )
{
\Neuron\Log\Log::error( 'Container initialization failed: ' . $e->getMessage() );

// Create a fallback settings with minimal configuration
// This allows the application to start even if advanced configuration fails
$yaml = new Yaml( "$configPath/neuron.yaml" );
$settings = new SettingManager( $yaml );
}

// Boot MVC application (initializers can now access Container from Registry)
$app = \Neuron\Mvc\boot( $configPath );
// Create MVC application with our configured SettingManager
// (Don't use MVC boot as it would create its own SettingManager without secrets)
$basePath = $settings->get( 'system', 'base_path' ) ?: getenv( 'SYSTEM_BASE_PATH' ) ?: '.';
$version = \Neuron\Data\Factories\Version::fromFile( "$basePath/.version.json" );
$app = new Application( $version->getAsString(), $settings );
Comment thread
cursor[bot] marked this conversation as resolved.

// Update container with Application instance and set on app
if( isset( $container ) )
Expand Down
Loading