diff --git a/config/bootstrap.php b/config/bootstrap.php
index 0adc2b8..363f9c4 100644
--- a/config/bootstrap.php
+++ b/config/bootstrap.php
@@ -7,13 +7,17 @@ declare(strict_types=1);
// Define all the important paths
define('APP_ROOT', dirname(dirname(__FILE__)));
+// Root-level directories
define('CONFIG_DIR', APP_ROOT . '/config');
+define('PUBLIC_DIR', APP_ROOT . '/public');
define('SRC_DIR', APP_ROOT . '/src');
define('STORAGE_DIR', APP_ROOT . '/storage');
-define('TEMPLATES_DIR', APP_ROOT . '/templates');
-define('DATA_DIR', STORAGE_DIR . '/db');
-define('DB_FILE', DATA_DIR . '/tkr.sqlite');
+// Storage subdirectories
define('CSS_UPLOAD_DIR', STORAGE_DIR . '/upload/css');
+define('DATA_DIR', STORAGE_DIR . '/db');
+define('TEMPLATES_DIR', APP_ROOT . '/templates');
+// Database file
+define('DB_FILE', DATA_DIR . '/tkr.sqlite');
// Janky autoloader function
// This is a bit more consistent with current frameworks
diff --git a/docker/apache/shared-hosting/.htaccess b/docker/apache/shared-hosting/.htaccess
index 9fd4083..c2bf33a 100644
--- a/docker/apache/shared-hosting/.htaccess
+++ b/docker/apache/shared-hosting/.htaccess
@@ -1,49 +1,19 @@
-# Example Apache VirtualHost
-# for serving tkr as a subdirectory path
-# on shared hosting via .htaccess
-#
-# e.g. http://www.my-domain.com/tkr
-#
-# This should work without modification if you extract the app
-# to /tkr from your web document root
+# Basic .htaccess for tkr on shared hosting
+# For use with included docker-compose.yml
# Enable mod_rewrite
RewriteEngine On
-# Security headers
-Header always set X-Frame-Options "SAMEORIGIN"
-Header always set X-XSS-Protection "1; mode=block"
-Header always set X-Content-Type-Options "nosniff"
-
-# Directory index
+# Set directory index
DirectoryIndex public/index.php
-# Security: Block direct access to .php files (except through rewrites)
-RewriteCond %{THE_REQUEST} \s/[^?\s]*\.php[\s?] [NC]
-RewriteRule ^.*$ - [R=404,L]
+# Block access to sensitive directories
+RewriteRule ^(storage|src|templates|config)(/.*)?$ - [F,L]
-# Security: Block access to sensitive directories
-RewriteRule ^(storage|src|templates|examples|config)(/.*)?$ - [F,L]
-
-# Security: Block access to hidden files
+# Block access to hidden files
RewriteRule ^\..*$ - [F,L]
-# Cache CSS files for 1 hour
-
- Header set Cache-Control "public, max-age=3600"
-
-
-# Serve the one static file that exists: css/tkr.css
-# (Pass requests to css/custom/ through to the PHP app)
-RewriteCond %{REQUEST_URI} !^/css/custom/
-RewriteRule ^css/tkr\.css$ public/css/tkr.css [L]
-
-# 404 all other static files (images, js, fonts, etc.)
-# so those requests don't hit the PHP app
-# (this is to reduce load on the PHP app from bots and scanners)
-RewriteRule \.(js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|pdf|zip|mp3|mp4|avi|mov)$ - [R=404,L]
-
-# Everything else goes to the front controller
+# Route everything else through public/index.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ public/index.php [L]
diff --git a/docker/apache/vps/root/tkr.my-domain.com.conf b/docker/apache/vps/root/tkr.my-domain.com.conf
index e699adf..99b7290 100644
--- a/docker/apache/vps/root/tkr.my-domain.com.conf
+++ b/docker/apache/vps/root/tkr.my-domain.com.conf
@@ -1,19 +1,21 @@
-# Example Apache VirtualHost
-# for serving tkr as a subdomain root without SSL
-# e.g. http://tkr.my-domain.com/
-#
-# NOTE: Do not use in production.
-# This is provided for docker compose
-# (The included docker-compose file will mount it in the container image)
+# Basic Apache VirtualHost for tkr
+# For use with included docker-compose.yml
+
+# HTTP - redirect to HTTPS
ServerName localhost
DocumentRoot /var/www/tkr/public
- # Security headers
- Header always set X-Frame-Options "SAMEORIGIN"
- Header always set X-XSS-Protection "1; mode=block"
- Header always set X-Content-Type-Options "nosniff"
- Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
+ # Main directory - route everything through index.php
+
+ AllowOverride None
+ Require all granted
+
+ RewriteEngine On
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteRule ^(.*)$ index.php [L]
+
# Block access to sensitive directories
@@ -22,59 +24,13 @@
Require all denied
-
- Require all denied
-
Require all denied
-
- # Block access to hidden files
-
+
Require all denied
-
-
- # Cache CSS files
-
- Header set Cache-Control "public, max-age=3600"
-
-
- # Serve static CSS file
- Alias /css/tkr.css /var/www/tkr/public/css/tkr.css
-
- # 404 all non-css static files (images, js, fonts, etc.)
- # so those requests don't hit the PHP app
- # (this is to reduce load on the PHP app from bots and scanners)
-
-
- Require all denied
-
-
-
- # Enable rewrite engine
-
- Options -Indexes
- AllowOverride None
- Require all granted
-
- RewriteEngine On
-
- # Block direct PHP access
- RewriteCond %{THE_REQUEST} \s/[^?\s]*\.php[\s?] [NC]
- RewriteRule ^.*$ - [R=404,L]
-
- # Serve the one static file that exists: css/tkr.css
- # (Pass requests to css/custom/ through to the PHP app)
- RewriteCond %{REQUEST_URI} !^/css/custom/
- RewriteRule ^css/tkr\.css$ css/tkr.css [L]
-
- # Everything else to front controller
- RewriteCond %{REQUEST_FILENAME} !-f
- RewriteCond %{REQUEST_FILENAME} !-d
- RewriteRule ^(.*)$ index.php [L]
- # Error and access logs
ErrorLog ${APACHE_LOG_DIR}/tkr_error.log
CustomLog ${APACHE_LOG_DIR}/tkr_access.log combined
diff --git a/docker/apache/vps/subfolder/my-domain.com.conf b/docker/apache/vps/subfolder/my-domain.com.conf
index b1b40d7..856d8ec 100644
--- a/docker/apache/vps/subfolder/my-domain.com.conf
+++ b/docker/apache/vps/subfolder/my-domain.com.conf
@@ -1,72 +1,38 @@
-# Example Apache VirtualHost
-# for serving tkr as a subdirectory path without SSL
-# e.g. http://www.my-domain.com/tkr
-#
-# NOTE: Do not use in production.
-# This is provided for docker compose
-# (The included docker-compose file will mount it in the container image)
+# Basic Apache config for tkr in subfolder
+# e.g. https://your-domain.com/tkr
+# For use with included docker-compose.yml
+
+# Alias for tkr subfolder
ServerName localhost
- DocumentRoot /var/www/html
-
- # Security headers
- Header always set X-Frame-Options "SAMEORIGIN"
- Header always set X-XSS-Protection "1; mode=block"
- Header always set X-Content-Type-Options "nosniff"
- Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
-
- # tkr Application at /tkr
- # NOTE: If you change the directory name,
- # remember to update all instances of /var/www/tkr in this file to match
Alias /tkr /var/www/tkr/public
- # Block access to sensitive TKR directories
+
+ AllowOverride None
+ Require all granted
+
+ # Front controller pattern
+ RewriteEngine On
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteRule ^(.*)$ /tkr/index.php [L]
+
+
+ # Block access to sensitive directories
Require all denied
Require all denied
-
- Require all denied
-
Require all denied
-
- # 404 all non-css static files in /tkr (images, js, fonts, etc.)
- # so those requests don't hit the PHP app
- # (this is to reduce load on the PHP app from bots and scanners)
-
-
- Require all denied
-
-
-
- # tkr application directory
-
- Options -Indexes
- AllowOverride None
- Require all granted
-
- RewriteEngine On
-
- # Block direct PHP access
- RewriteCond %{THE_REQUEST} \s/[^?\s]*\.php[\s?] [NC]
- RewriteRule ^.*$ - [R=404,L]
-
- # Serve the one static file that exists: css/tkr.css
- # (Pass requests to css/custom/ through to the PHP app)
- RewriteCond %{REQUEST_URI} !^/tkr/css/custom/
- RewriteRule ^css/tkr\.css$ css/tkr.css [L]
-
- # Send everything else to the front controller
- RewriteCond %{REQUEST_FILENAME} !-f
- RewriteCond %{REQUEST_FILENAME} !-d
- RewriteRule ^(.*)$ index.php [L]
+
+ Require all denied
# Error and access logs
- ErrorLog ${APACHE_LOG_DIR}/my-domain_error.log
- CustomLog ${APACHE_LOG_DIR}/my-domain_access.log combined
-
+ ErrorLog ${APACHE_LOG_DIR}/tkr_error.log
+ CustomLog ${APACHE_LOG_DIR}/tkr_access.log combined
+
\ No newline at end of file
diff --git a/docker/nginx/root/nginx.conf b/docker/nginx/root/nginx.conf
index 8d01eef..f40851f 100644
--- a/docker/nginx/root/nginx.conf
+++ b/docker/nginx/root/nginx.conf
@@ -1,100 +1,45 @@
-# Example nginx config
-# for serving tkr as a subdomain without SSL
-# e.g. http://tkr.my-domain.com/
-#
-# NOTE: Do not use in production.
-# This is provided for docker compose
-# (The included docker-compose file will mount it in the container image)
+# Basic nginx config for tkr
+# Replace "your-domain.com" with your actual domain
+# Replace "/var/www/tkr" with your installation path
+
+# HTTP - redirect to HTTPS
server {
- listen 80;
+ listen 80 default_server;
server_name localhost;
root /var/www/tkr/public;
index index.php;
- # Security headers
- # The first rule is to prevent including in a frame on a different domain.
- # Remove it if you want to do that.
- add_header X-Frame-Options "SAMEORIGIN" always;
- add_header X-XSS-Protection "1; mode=block" always;
- add_header X-Content-Type-Options "nosniff" always;
+ # Block access to sensitive directories
+ location ~ ^/(storage|src|templates|config) {
+ deny all;
+ return 404;
+ }
- # Deny access to hidden files
+ # Block access to hidden files
location ~ /\. {
deny all;
- access_log off;
- log_not_found off;
}
- # PHP routing - everything goes through index.php
- location / {
- # Cache static files
- # Note that I don't actually serve most of this (just css)
- # but this prevents requests for static content from getting to the PHP handler.
- #
- # I've excluded /css/custom so that requests for uploaded css can be handled by the PHP app.
- # That lets me store uploaded content outside of the document root,
- # so it isn't served directly.
-
- # CSS files - 1 hour cache
- location ~* ^/(?!css/custom/).+\.css$ {
- expires 1h;
- add_header Cache-Control "public";
- try_files $uri =404;
- }
-
- # Other static assets - 1 year cache
- location ~* ^/.+\.(js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
- expires 1y;
- add_header Cache-Control "public, immutable";
- try_files $uri =404;
- }
-
- # index.php is the entry point
- # It needs to be sent to php-fpm
- # But if someone tries to directly access index.php, that file will throw a 404
- # so bots and scanners can't tell this is a php app
- location = /index.php {
- # If you're running php-fpm on the same server as nginx,
- # then change this to the local php-fpm socket
- # e.g. fastcgi_pass unix:/run/php/php8.2-fpm.sock;
- fastcgi_pass php:9000;
- fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
- include fastcgi_params;
-
- fastcgi_param REQUEST_METHOD $request_method;
- fastcgi_param REQUEST_URI $request_uri;
- fastcgi_param QUERY_STRING $query_string;
- }
-
- # Block attempts to access all other .php files directly
- # (these are bots and scanners)
- location ~ ^/.+\.php$ {
- return 404;
- }
-
- # forward other requests to the fallback block,
- # which sends them to php-fpm for handling
- try_files $uri $uri/ @tkr_fallback;
- }
-
- # Fallback for /tkr routing - all non-file requests (e.g. /login) go to index.php
- location @tkr_fallback {
- # If you're running php-fpm on the same server as nginx,
- # then change this to the local php-fpm socket
- # e.g. fastcgi_pass unix:/run/php/php8.2-fpm.sock;
+ # Handle PHP files
+ location ~ \.php$ {
fastcgi_pass php:9000;
fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
include fastcgi_params;
+ }
+ # Front controller pattern - route everything else through index.php
+ location / {
+ try_files $uri $uri/ @tkr;
+ }
+
+ # Handle PHP requests
+ location @tkr {
+ fastcgi_pass php:9000;
+ fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
+ include fastcgi_params;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param QUERY_STRING $query_string;
}
-
- # Deny access to sensitive directories
- location ~ ^/(storage|src|templates|uploads|config) {
- deny all;
- return 404;
- }
}
diff --git a/docker/nginx/subfolder/nginx.conf b/docker/nginx/subfolder/nginx.conf
index b6f8d84..b381910 100644
--- a/docker/nginx/subfolder/nginx.conf
+++ b/docker/nginx/subfolder/nginx.conf
@@ -1,101 +1,47 @@
-# Example nginx config
-# for serving tkr as a subdfolder without SSL
-# e.g. http://my-domain.com/tkr
-#
-# NOTE: Do not use in production.
-# This is provided for docker compose
-# (The included docker-compose file will mount it in the container image)
+# Basic nginx config for tkr in subfolder
+# e.g. https://your-domain.com/tkr
+# For use with included docker-compose.yml
+
server {
listen 80 default_server;
- listen [::]:80 default_server;
-
- # replace localhost with your subdomain
- # e.g. tkr.my-domain.com
server_name localhost;
root /var/www/html;
index index.html;
- # Security headers
- # The first rule is to prevent including in a frame on a different domain.
- # Remove it if you want to do that.
- add_header X-Frame-Options "SAMEORIGIN" always;
- add_header X-XSS-Protection "1; mode=block" always;
- add_header X-Content-Type-Options "nosniff" always;
-
- # Deny access to hidden files
- location ~ /\. {
- deny all;
- access_log off;
- log_not_found off;
- }
-
- # PHP routing - everything under /tkr goes through index.php
location /tkr {
alias /var/www/tkr/public;
index index.php;
- # Cache static files
- # Note that I don't actually serve most of this (just css)
- # but this prevents requests for static content from getting to the PHP handler.
- #
- # I've excluded /css/custom so that requests for uploaded css can be handled by the PHP app.
- # That lets me store uploaded content outside of the document root,
- # so it isn't served directly.
-
- # CSS files - 1 hour cache
- location ~* ^/tkr/(?!css/custom/).+\.css$ {
- expires 1h;
- add_header Cache-Control "public";
- try_files $uri =404;
- }
-
- # Other static assets - 1 year cache
- location ~* ^/tkr/.+\.(js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
- expires 1y;
- add_header Cache-Control "public, immutable";
- try_files $uri =404;
- }
-
- # index.php is the entry point
- # It needs to be sent to php-fpm
- # But if someone tries to directly access index.php, that file will throw a 404
- # so bots and scanners can't tell this is a php app
- location = /tkr/index.php {
- fastcgi_pass php:9000;
- fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
- include fastcgi_params;
-
- fastcgi_param REQUEST_METHOD $request_method;
- fastcgi_param REQUEST_URI $request_uri;
- fastcgi_param QUERY_STRING $query_string;
- }
-
- # Block attempts to access all other .php files directly
- # (these are bots and scanners)
- location ~ ^/tkr/.+\.php$ {
+ # Block access to sensitive directories
+ location ~ ^/tkr/(storage|src|templates|config) {
+ deny all;
return 404;
}
- # forward other requests to the fallback block,
- # which sends them to php-fpm for handling
- try_files $uri $uri/ @tkr_fallback;
+ # Block access to hidden files
+ location ~ /\. {
+ deny all;
+ }
+
+ # Handle PHP files
+ location ~ \.php$ {
+ fastcgi_pass php:9000;
+ fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
+ include fastcgi_params;
+ }
+
+ # Front controller pattern
+ # Send everything else to index.php
+ try_files $uri $uri/ @tkr;
}
- # Fallback for /tkr routing - all non-file requests (e.g. /login) go to index.php
- location @tkr_fallback {
+ location @tkr {
fastcgi_pass php:9000;
fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
include fastcgi_params;
-
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param QUERY_STRING $query_string;
}
-
- # Deny access to sensitive directories
- location ~ ^/tkr/(storage|src|templates|uploads|config) {
- deny all;
- return 404;
- }
}
\ No newline at end of file
diff --git a/examples/apache/shared-hosting/.htaccess b/examples/apache/shared-hosting/.htaccess
index 9fd4083..65e699e 100644
--- a/examples/apache/shared-hosting/.htaccess
+++ b/examples/apache/shared-hosting/.htaccess
@@ -1,49 +1,20 @@
-# Example Apache VirtualHost
-# for serving tkr as a subdirectory path
-# on shared hosting via .htaccess
-#
-# e.g. http://www.my-domain.com/tkr
-#
-# This should work without modification if you extract the app
-# to /tkr from your web document root
+# Basic .htaccess for tkr on shared hosting
+# Place this file inside the tkr directory
+# e.g. if tkr is at /public_html/tkr/, this goes in /public_html/tkr/
# Enable mod_rewrite
RewriteEngine On
-# Security headers
-Header always set X-Frame-Options "SAMEORIGIN"
-Header always set X-XSS-Protection "1; mode=block"
-Header always set X-Content-Type-Options "nosniff"
-
-# Directory index
+# Set directory index
DirectoryIndex public/index.php
-# Security: Block direct access to .php files (except through rewrites)
-RewriteCond %{THE_REQUEST} \s/[^?\s]*\.php[\s?] [NC]
-RewriteRule ^.*$ - [R=404,L]
+# Block access to sensitive directories
+RewriteRule ^(storage|src|templates|config)(/.*)?$ - [F,L]
-# Security: Block access to sensitive directories
-RewriteRule ^(storage|src|templates|examples|config)(/.*)?$ - [F,L]
-
-# Security: Block access to hidden files
+# Block access to hidden files
RewriteRule ^\..*$ - [F,L]
-# Cache CSS files for 1 hour
-
- Header set Cache-Control "public, max-age=3600"
-
-
-# Serve the one static file that exists: css/tkr.css
-# (Pass requests to css/custom/ through to the PHP app)
-RewriteCond %{REQUEST_URI} !^/css/custom/
-RewriteRule ^css/tkr\.css$ public/css/tkr.css [L]
-
-# 404 all other static files (images, js, fonts, etc.)
-# so those requests don't hit the PHP app
-# (this is to reduce load on the PHP app from bots and scanners)
-RewriteRule \.(js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|pdf|zip|mp3|mp4|avi|mov)$ - [R=404,L]
-
-# Everything else goes to the front controller
+# Route everything else through the front controller
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ public/index.php [L]
diff --git a/examples/apache/vps/root/tkr.my-domain.com.conf b/examples/apache/vps/root/tkr.my-domain.com.conf
index 753a7af..9da6844 100644
--- a/examples/apache/vps/root/tkr.my-domain.com.conf
+++ b/examples/apache/vps/root/tkr.my-domain.com.conf
@@ -1,39 +1,34 @@
-# Example Apache VirtualHost
-# for serving tkr as a subdomain root with SSL
-# e.g. https://tkr.my-domain.com/
-#
-# Use SSL in production.
-# This is a minimal SSL confiuration
-# For more robust SSL configuration, refer to https://ssl-config.mozilla.org/
+# Basic Apache VirtualHost for tkr
+# Replace "your-domain.com" with your actual domain
+# Replace "/var/www/tkr" with your installation path
+
+# HTTP - redirect to HTTPS
- # CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com
- ServerName localhost
- # CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com
- DocumentRoot /var/www/tkr/public
- # Redirect HTTP to HTTPS
- Redirect permanent / https://tkr.my-domain.com/
+ ServerName your-domain.com
+ Redirect permanent / https://your-domain.com/
+# HTTPS
- # CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com
- ServerName localhost
- # CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com
+ ServerName your-domain.com
DocumentRoot /var/www/tkr/public
-
- # SSL Configuration
+
+ # SSL Configuration (using Let's Encrypt)
SSLEngine on
-
- # Assumes you're using letsencrypt for cert generation
- # Replace with the actual paths to your cert and key
- SSLCertificateFile /etc/letsencrypt/live/tkr.my-domain.com/fullchain.pem
- SSLCertificateKeyFile /etc/letsencrypt/live/tkr.my-domain.com/privkey.pem
-
- # Security headers
- Header always set X-Frame-Options "SAMEORIGIN"
- Header always set X-XSS-Protection "1; mode=block"
- Header always set X-Content-Type-Options "nosniff"
- Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
-
+ SSLCertificateFile /etc/letsencrypt/live/your-domain.com/fullchain.pem
+ SSLCertificateKeyFile /etc/letsencrypt/live/your-domain.com/privkey.pem
+
+ # Main directory - route everything through index.php
+
+ AllowOverride None
+ Require all granted
+
+ RewriteEngine On
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteRule ^(.*)$ index.php [L]
+
+
# Block access to sensitive directories
Require all denied
@@ -41,59 +36,13 @@
Require all denied
-
- Require all denied
-
Require all denied
-
- # Block access to hidden files
-
+
Require all denied
-
-
- # Cache CSS files
-
- Header set Cache-Control "public, max-age=3600"
-
-
- # Serve static CSS file
- Alias /css/tkr.css /var/www/tkr/public/css/tkr.css
-
- # 404 all non-css static files (images, js, fonts, etc.)
- # so those requests don't hit the PHP app
- # (this is to reduce load on the PHP app from bots and scanners)
-
-
- Require all denied
-
-
-
- # Enable rewrite engine
-
- Options -Indexes
- AllowOverride None
- Require all granted
-
- RewriteEngine On
-
- # Block direct PHP access
- RewriteCond %{THE_REQUEST} \s/[^?\s]*\.php[\s?] [NC]
- RewriteRule ^.*$ - [R=404,L]
-
- # Serve the one static file that exists: css/tkr.css
- # (Pass requests to css/custom/ through to the PHP app)
- RewriteCond %{REQUEST_URI} !^/css/custom/
- RewriteRule ^css/tkr\.css$ css/tkr.css [L]
-
- # Everything else to front controller
- RewriteCond %{REQUEST_FILENAME} !-f
- RewriteCond %{REQUEST_FILENAME} !-d
- RewriteRule ^(.*)$ index.php [L]
-
- # Error and access logs
+
ErrorLog ${APACHE_LOG_DIR}/tkr_error.log
CustomLog ${APACHE_LOG_DIR}/tkr_access.log combined
diff --git a/examples/apache/vps/subfolder/my-domain.com.conf b/examples/apache/vps/subfolder/my-domain.com.conf
index b2d42ea..8d14e94 100644
--- a/examples/apache/vps/subfolder/my-domain.com.conf
+++ b/examples/apache/vps/subfolder/my-domain.com.conf
@@ -1,91 +1,31 @@
-# Example Apache VirtualHost
-# for serving tkr as a subdirectory path with SSL
-# e.g. https://www.my-domain.com/tkr
-#
-# Use SSL in production.
-# This is a minimal SSL confiuration
-# For more robust SSL configuration, refer to https://ssl-config.mozilla.org/
-
- # CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com
- ServerName localhost
- # CONFIG: Replace with your subdomain, e.g. tkr.my-domain.com
- DocumentRoot /var/www/tkr
- # Redirect HTTP to HTTPS
- Redirect permanent / https://my-domain.com/
-
+# Basic Apache config for tkr in subfolder
+# e.g. https://your-domain.com/tkr
+# Add this to your existing VirtualHost configuration
-
- # CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com
- ServerName localhost
- # CONFIG: Replace with your subdomain, e.g. tkr.my-domain.com
- DocumentRoot /var/www/tkr/
+# Alias for tkr subfolder
+Alias /tkr /var/www/tkr/public
- # SSL Configuration
- SSLEngine on
+
+ AllowOverride None
+ Require all granted
+
+ # Front controller pattern
+ RewriteEngine On
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteRule ^(.*)$ /tkr/index.php [L]
+
- # Assumes you're using letsencrypt for cert generation
- # Replace with the actual paths to your cert and key
- SSLCertificateFile /etc/letsencrypt/live/my-domain.com/fullchain.pem
- SSLCertificateKeyFile /etc/letsencrypt/live/my-domain.com/privkey.pem
-
- # Security headers
- Header always set X-Frame-Options "SAMEORIGIN"
- Header always set X-XSS-Protection "1; mode=block"
- Header always set X-Content-Type-Options "nosniff"
- Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
-
- # tkr Application at /tkr
- # NOTE: If you change the directory name,
- # remember to update all instances of /var/www/tkr in this file to match
- Alias /tkr /var/www/tkr/public
-
- # Block access to sensitive TKR directories
-
- Require all denied
-
-
- Require all denied
-
-
- Require all denied
-
-
- Require all denied
-
-
- # 404 all non-css static files in /tkr (images, js, fonts, etc.)
- # so those requests don't hit the PHP app
- # (this is to reduce load on the PHP app from bots and scanners)
-
-
- Require all denied
-
-
-
- # tkr application directory
-
- Options -Indexes
- AllowOverride None
- Require all granted
-
- RewriteEngine On
-
- # Block direct PHP access
- RewriteCond %{THE_REQUEST} \s/[^?\s]*\.php[\s?] [NC]
- RewriteRule ^.*$ - [R=404,L]
-
- # Serve the one static file that exists: css/tkr.css
- # (Pass requests to css/custom/ through to the PHP app)
- RewriteCond %{REQUEST_URI} !^/tkr/css/custom/
- RewriteRule ^css/tkr\.css$ css/tkr.css [L]
-
- # Send everything else to the front controller
- RewriteCond %{REQUEST_FILENAME} !-f
- RewriteCond %{REQUEST_FILENAME} !-d
- RewriteRule ^(.*)$ index.php [L]
-
-
- # Error and access logs
- ErrorLog ${APACHE_LOG_DIR}/my-domain_error.log
- CustomLog ${APACHE_LOG_DIR}/my-domain_access.log combined
-
+# Block access to sensitive directories
+
+ Require all denied
+
+
+ Require all denied
+
+
+ Require all denied
+
+
+ Require all denied
+
\ No newline at end of file
diff --git a/examples/nginx/root/nginx.conf b/examples/nginx/root/nginx.conf
index c7855a1..717efa1 100644
--- a/examples/nginx/root/nginx.conf
+++ b/examples/nginx/root/nginx.conf
@@ -1,117 +1,51 @@
-# Example nginx config
-# for serving tkr as a subdomain with SSL
-# e.g. https://tkr.my-domain.com/
-#
-# Use SSL in production.
-# This is a minimal SSL confiuration
-# For more robust SSL configuration, refer to https://ssl-config.mozilla.org/
+# Basic nginx config for tkr
+# Replace "your-domain.com" with your actual domain
+# Replace "/var/www/tkr" with your installation path
+
+# HTTP - redirect to HTTPS
+server {
+ listen 80;
+ listen [::]:80;
+ server_name your-domain.com;
+ return 301 https://$host$request_uri;
+}
+
+# HTTPS
server {
listen 443 ssl;
listen [::]:443 ssl;
-
- # CONFIG: replace "localhost" with your subdomain (e.g. tkr.my-domain.com)
- server_name localhost;
-
- # CONFIG:
- # replace "/var/www/tkr" with the directory you extracted the .zip file to (if different)
+
+ server_name your-domain.com;
root /var/www/tkr/public;
index index.php;
-
- # CONFIG:
- # Assumes you're using letsencrypt for cert generation
- # Replace with the actual paths to your cert and key
- ssl_certificate /etc/letsencrypt/live/tkr.my-domain.com/fullchain.pem;
- ssl_certificate_key /etc/letsencrypt/live/tkr.my-domain.com/privkey.pem;
-
- # Security headers
- # The first rule is to prevent including in a frame on a different domain.
- # Remove it if you want to do that.
- add_header X-Frame-Options "SAMEORIGIN" always;
- add_header X-XSS-Protection "1; mode=block" always;
- add_header X-Content-Type-Options "nosniff" always;
-
- # Deny access to hidden files
+
+ # SSL Configuration (using Let's Encrypt)
+ ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
+
+ # Block access to sensitive directories
+ location ~ ^/(storage|src|templates|config) {
+ deny all;
+ return 404;
+ }
+
+ # Block access to hidden files
location ~ /\. {
deny all;
- access_log off;
- log_not_found off;
}
-
- # PHP routing - everything goes through index.php
+
+ # Front controller pattern - route everything through index.php
location / {
- # Cache static files
- # Note that I don't actually serve most of this (just css)
- # but this prevents requests for static content from getting to the PHP handler.
- #
- # I've excluded /css/custom so that requests for uploaded css can be handled by the PHP app.
- # That lets me store uploaded content outside of the document root,
- # so it isn't served directly.
- location ~* ^/(?!css/custom/).+\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
- expires 1y;
- add_header Cache-Control "public, immutable";
- try_files $uri =404;
- }
-
- # index.php is the entry point
- # It needs to be sent to php-fpm
- # But if someone tries to directly access index.php, that file will throw a 404
- # so bots and scanners can't tell this is a php app
- location = /index.php {
- # CONFIG:
- # If you're running php-fpm on the same server as nginx,
- # then change this to the local php-fpm socket
- # e.g. fastcgi_pass unix:/run/php/php8.2-fpm.sock;
- fastcgi_pass php:9000;
-
- # CONFIG:
- # replace "/var/www/tkr" with the directory you extracted the .zip file to (if different)
- fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
- include fastcgi_params;
-
- fastcgi_param REQUEST_METHOD $request_method;
- fastcgi_param REQUEST_URI $request_uri;
- fastcgi_param QUERY_STRING $query_string;
- }
-
- # Block attempts to access all other .php files directly
- # (these are bots and scanners)
- location ~ ^/.+\.php$ {
- return 404;
- }
-
- # forward other requests to the fallback block,
- # which sends them to php-fpm for handling
- try_files $uri $uri/ @tkr_fallback;
+ try_files $uri $uri/ @tkr;
}
-
- # Fallback for /tkr routing - all non-file requests (e.g. /login) go to index.php
- location @tkr_fallback {
- # CONFIG:
- # If you're running php-fpm on the same server as nginx,
- # then change this to the local php-fpm socket
- # e.g. fastcgi_pass unix:/run/php/php8.2-fpm.sock;
- fastcgi_pass php:9000;
-
- # CONFIG:
- # replace "/var/www/tkr" with the directory you extracted the .zip file to (if different)
+
+ # Handle PHP requests
+ location @tkr {
+ fastcgi_pass unix:/run/php/php8.2-fpm.sock; # Adjust PHP version as needed
fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
include fastcgi_params;
-
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param QUERY_STRING $query_string;
}
-
- # Deny access to sensitive directories
- location ~ ^/(storage|src|templates|uploads|config) {
- deny all;
- return 404;
- }
-}
-
-server {
- listen 80 default_server;
- listen [::]:80 default_server;
-
- return 301 https://$host$request_uri;
}
diff --git a/examples/nginx/subfolder/nginx.conf b/examples/nginx/subfolder/nginx.conf
index 3095e2b..1fb18b8 100644
--- a/examples/nginx/subfolder/nginx.conf
+++ b/examples/nginx/subfolder/nginx.conf
@@ -1,118 +1,38 @@
-# Example nginx config
-# for serving tkr as a subdfolder with SSL
-# e.g. https://my-domain.com/tkr
-#
-# Use SSL in production.
-# This is a minimal SSL confiuration
-# For more robust SSL configuration, refer to https://ssl-config.mozilla.org/
-server {
- listen 443 ssl;
- listen [::]:443 ssl;
+# Basic nginx config for tkr in subfolder
+# e.g. https://your-domain.com/tkr
+# Add this location block to your existing server configuration
- # CONFIG: Replace localhost with your domain e.g. my-domain.com
- server_name localhost;
+location /tkr {
+ alias /var/www/tkr/public;
+ index index.php;
- # CONFIG:
- # Assumes you're using letsencrypt for cert generation
- # Replace with the actual paths to your cert and key
- ssl_certificate /etc/letsencrypt/live/my-domain.com/fullchain.pem;
- ssl_certificate_key /etc/letsencrypt/live/my-domain.com/privkey.pem;
-
- # Security headers
- # The first rule is to prevent including in a frame on a different domain.
- # Remove it if you want to do that.
- add_header X-Frame-Options "SAMEORIGIN" always;
- add_header X-XSS-Protection "1; mode=block" always;
- add_header X-Content-Type-Options "nosniff" always;
-
- # Deny access to hidden files
- location ~ /\. {
- deny all;
- access_log off;
- log_not_found off;
- }
-
- # PHP routing - everything under /tkr goes through index.php
- location /tkr {
- # CONFIG:
- # replace "/var/www/tkr" with the directory you extracted the .zip file to (if different)
- alias /var/www/tkr/public;
- index index.php;
-
- # Cache static files
- # Note that I don't actually serve most of this (just css)
- # but this prevents requests for static content from getting to the PHP handler.
- #
- # I've excluded /css/custom so that requests for uploaded css can be handled by the PHP app.
- # That lets me store uploaded content outside of the document root,
- # so it isn't served directly.
- location ~* ^/tkr/(?!css/custom/).+\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
- expires 1y;
- add_header Cache-Control "public, immutable";
- try_files $uri =404;
- }
-
- # index.php is the entry point
- # It needs to be sent to php-fpm
- # But if someone tries to directly access index.php, that file will throw a 404
- # so bots and scanners can't tell this is a php app
- location = /tkr/index.php {
- # CONFIG:
- # If you're running php-fpm on the same server as nginx,
- # then change this to the local php-fpm socket
- # e.g. fastcgi_pass unix:/run/php/php8.2-fpm.sock;
- fastcgi_pass php:9000;
-
- # CONFIG:
- # replace "/var/www/tkr" with the directory you extracted the .zip file to (if different)
- fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
- include fastcgi_params;
-
- fastcgi_param REQUEST_METHOD $request_method;
- fastcgi_param REQUEST_URI $request_uri;
- fastcgi_param QUERY_STRING $query_string;
- }
-
- # Block attempts to access all other .php files directly
- # (these are bots and scanners)
- location ~ ^/tkr/.+\.php$ {
- return 404;
- }
-
- # forward other requests to the fallback block,
- # which sends them to php-fpm for handling
- try_files $uri $uri/ @tkr_fallback;
- }
-
- # Fallback for /tkr routing - all non-file requests (e.g. /login) go to index.php
- location @tkr_fallback {
- # CONFIG:
- # If you're running php-fpm on the same server as nginx,
- # then change this to the local php-fpm socket
- # e.g. fastcgi_pass unix:/run/php/php8.2-fpm.sock;
- fastcgi_pass php:9000;
-
- # CONFIG:
- # replace "/var/www/tkr" with the directory you extracted the .zip file to (if different)
- fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
+ # Handle PHP files
+ location ~ \.php$ {
+ fastcgi_pass unix:/run/php/php8.2-fpm.sock; # Adjust PHP version/socket as needed
fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
include fastcgi_params;
-
- fastcgi_param REQUEST_METHOD $request_method;
- fastcgi_param REQUEST_URI $request_uri;
- fastcgi_param QUERY_STRING $query_string;
}
- # Deny access to sensitive directories
- location ~ ^/tkr/(storage|src|templates|uploads|config) {
+ # Block access to sensitive directories
+ location ~ ^/tkr/(storage|src|templates|config) {
deny all;
return 404;
}
+
+ # Block access to hidden files
+ location ~ /\. {
+ deny all;
+ }
+
+ # Front controller pattern
+ try_files $uri $uri/ @tkr;
}
-server {
- listen 80 default_server;
- listen [::]:80 default_server;
-
- return 301 https://$host$request_uri;
-}
+location @tkr {
+ fastcgi_pass unix:/run/php/php8.2-fpm.sock; # Adjust PHP version as needed
+ fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
+ include fastcgi_params;
+ fastcgi_param REQUEST_METHOD $request_method;
+ fastcgi_param REQUEST_URI $request_uri;
+ fastcgi_param QUERY_STRING $query_string;
+}
\ No newline at end of file
diff --git a/examples/production/apache/.htaccess b/examples/production/apache/.htaccess
new file mode 100644
index 0000000..c1637c0
--- /dev/null
+++ b/examples/production/apache/.htaccess
@@ -0,0 +1,49 @@
+# Example Apache VirtualHost
+# for serving tkr as a subdirectory path
+# on shared hosting via .htaccess
+#
+# e.g. http://www.my-domain.com/tkr
+#
+# This should work without modification if you extract the app
+# to /tkr from your web document root
+
+# Enable mod_rewrite
+RewriteEngine On
+
+# Security headers
+Header always set X-Frame-Options "SAMEORIGIN"
+Header always set X-XSS-Protection "1; mode=block"
+Header always set X-Content-Type-Options "nosniff"
+
+# Directory index
+DirectoryIndex public/index.php
+
+# Security: Block direct access to .php files (except through rewrites)
+RewriteCond %{THE_REQUEST} \s/[^?\s]*\.php[\s?] [NC]
+RewriteRule ^.*$ - [R=404,L]
+
+# Security: Block access to sensitive directories
+RewriteRule ^(storage|src|templates|examples|config)(/.*)?$ - [F,L]
+
+# Security: Block access to hidden files
+RewriteRule ^\..*$ - [F,L]
+
+# Cache CSS files for 1 hour
+
+ Header set Cache-Control "public, max-age=3600"
+
+
+# Serve the one static file that exists: css/default.css
+# (Pass requests to css/custom/ through to the PHP app)
+RewriteCond %{REQUEST_URI} !^/css/custom/
+RewriteRule ^css/default\.css$ public/css/default.css [L]
+
+# 404 all other static files (images, js, fonts, etc.)
+# so those requests don't hit the PHP app
+# (this is to reduce load on the PHP app from bots and scanners)
+RewriteRule \.(js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|pdf|zip|mp3|mp4|avi|mov)$ - [R=404,L]
+
+# Everything else goes to the front controller
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteCond %{REQUEST_FILENAME} !-d
+RewriteRule ^(.*)$ public/index.php [L]
diff --git a/examples/production/apache/subfolder.conf b/examples/production/apache/subfolder.conf
new file mode 100644
index 0000000..e3fb161
--- /dev/null
+++ b/examples/production/apache/subfolder.conf
@@ -0,0 +1,91 @@
+# Example Apache VirtualHost
+# for serving tkr as a subdirectory path with SSL
+# e.g. https://www.my-domain.com/tkr
+#
+# Use SSL in production.
+# This is a minimal SSL confiuration
+# For more robust SSL configuration, refer to https://ssl-config.mozilla.org/
+
+ # CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com
+ ServerName localhost
+ # CONFIG: Replace with your subdomain, e.g. tkr.my-domain.com
+ DocumentRoot /var/www/tkr
+ # Redirect HTTP to HTTPS
+ Redirect permanent / https://my-domain.com/
+
+
+
+ # CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com
+ ServerName localhost
+ # CONFIG: Replace with your subdomain, e.g. tkr.my-domain.com
+ DocumentRoot /var/www/tkr/
+
+ # SSL Configuration
+ SSLEngine on
+
+ # Assumes you're using letsencrypt for cert generation
+ # Replace with the actual paths to your cert and key
+ SSLCertificateFile /etc/letsencrypt/live/my-domain.com/fullchain.pem
+ SSLCertificateKeyFile /etc/letsencrypt/live/my-domain.com/privkey.pem
+
+ # Security headers
+ Header always set X-Frame-Options "SAMEORIGIN"
+ Header always set X-XSS-Protection "1; mode=block"
+ Header always set X-Content-Type-Options "nosniff"
+ Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
+
+ # tkr Application at /tkr
+ # NOTE: If you change the directory name,
+ # remember to update all instances of /var/www/tkr in this file to match
+ Alias /tkr /var/www/tkr/public
+
+ # Block access to sensitive TKR directories
+
+ Require all denied
+
+
+ Require all denied
+
+
+ Require all denied
+
+
+ Require all denied
+
+
+ # 404 all non-css static files in /tkr (images, js, fonts, etc.)
+ # so those requests don't hit the PHP app
+ # (this is to reduce load on the PHP app from bots and scanners)
+
+
+ Require all denied
+
+
+
+ # tkr application directory
+
+ Options -Indexes
+ AllowOverride None
+ Require all granted
+
+ RewriteEngine On
+
+ # Block direct PHP access
+ RewriteCond %{THE_REQUEST} \s/[^?\s]*\.php[\s?] [NC]
+ RewriteRule ^.*$ - [R=404,L]
+
+ # Serve the one static file that exists: css/default.css
+ # (Pass requests to css/custom/ through to the PHP app)
+ RewriteCond %{REQUEST_URI} !^/tkr/css/custom/
+ RewriteRule ^css/default\.css$ css/tkr.css [L]
+
+ # Send everything else to the front controller
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteRule ^(.*)$ index.php [L]
+
+
+ # Error and access logs
+ ErrorLog ${APACHE_LOG_DIR}/my-domain_error.log
+ CustomLog ${APACHE_LOG_DIR}/my-domain_access.log combined
+
diff --git a/examples/production/apache/tkr.my-domain.com.conf b/examples/production/apache/tkr.my-domain.com.conf
new file mode 100644
index 0000000..3f64ec6
--- /dev/null
+++ b/examples/production/apache/tkr.my-domain.com.conf
@@ -0,0 +1,99 @@
+# Example Apache VirtualHost
+# for serving tkr as a subdomain root with SSL
+# e.g. https://tkr.my-domain.com/
+#
+# Use SSL in production.
+# This is a minimal SSL confiuration
+# For more robust SSL configuration, refer to https://ssl-config.mozilla.org/
+
+ # CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com
+ ServerName localhost
+ # CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com
+ DocumentRoot /var/www/tkr/public
+ # Redirect HTTP to HTTPS
+ Redirect permanent / https://tkr.my-domain.com/
+
+
+
+ # CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com
+ ServerName localhost
+ # CONFIG: Replace localhost with your subdomain, e.g. tkr.my-domain.com
+ DocumentRoot /var/www/tkr/public
+
+ # SSL Configuration
+ SSLEngine on
+
+ # Assumes you're using letsencrypt for cert generation
+ # Replace with the actual paths to your cert and key
+ SSLCertificateFile /etc/letsencrypt/live/tkr.my-domain.com/fullchain.pem
+ SSLCertificateKeyFile /etc/letsencrypt/live/tkr.my-domain.com/privkey.pem
+
+ # Security headers
+ Header always set X-Frame-Options "SAMEORIGIN"
+ Header always set X-XSS-Protection "1; mode=block"
+ Header always set X-Content-Type-Options "nosniff"
+ Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
+
+ # Block access to sensitive directories
+
+ Require all denied
+
+
+ Require all denied
+
+
+ Require all denied
+
+
+ Require all denied
+
+
+ # Block access to hidden files
+
+ Require all denied
+
+
+ # Cache CSS files
+
+ Header set Cache-Control "public, max-age=3600"
+
+
+ # Serve static CSS file
+ Alias /css/tkr.css /var/www/tkr/public/css/tkr.css
+
+ # 404 all non-css static files (images, js, fonts, etc.)
+ # so those requests don't hit the PHP app
+ # (this is to reduce load on the PHP app from bots and scanners)
+
+
+ Require all denied
+
+
+
+ # Enable rewrite engine
+
+ Options -Indexes
+ AllowOverride None
+ Require all granted
+
+ RewriteEngine On
+
+ # Block direct PHP access
+ RewriteCond %{THE_REQUEST} \s/[^?\s]*\.php[\s?] [NC]
+ RewriteRule ^.*$ - [R=404,L]
+
+ # Serve the one static file that exists: css/default.css
+ # (Pass requests to css/custom/ through to the PHP app)
+ RewriteCond %{REQUEST_URI} !^/css/custom/
+ RewriteRule ^css/default\.css$ css/default.css [L]
+
+ # Everything else to front controller
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteRule ^(.*)$ index.php [L]
+
+
+ # Error and access logs
+ ErrorLog ${APACHE_LOG_DIR}/tkr_error.log
+ CustomLog ${APACHE_LOG_DIR}/tkr_access.log combined
+
diff --git a/examples/production/nginx/nginx.conf b/examples/production/nginx/nginx.conf
new file mode 100644
index 0000000..c7855a1
--- /dev/null
+++ b/examples/production/nginx/nginx.conf
@@ -0,0 +1,117 @@
+# Example nginx config
+# for serving tkr as a subdomain with SSL
+# e.g. https://tkr.my-domain.com/
+#
+# Use SSL in production.
+# This is a minimal SSL confiuration
+# For more robust SSL configuration, refer to https://ssl-config.mozilla.org/
+server {
+ listen 443 ssl;
+ listen [::]:443 ssl;
+
+ # CONFIG: replace "localhost" with your subdomain (e.g. tkr.my-domain.com)
+ server_name localhost;
+
+ # CONFIG:
+ # replace "/var/www/tkr" with the directory you extracted the .zip file to (if different)
+ root /var/www/tkr/public;
+ index index.php;
+
+ # CONFIG:
+ # Assumes you're using letsencrypt for cert generation
+ # Replace with the actual paths to your cert and key
+ ssl_certificate /etc/letsencrypt/live/tkr.my-domain.com/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/tkr.my-domain.com/privkey.pem;
+
+ # Security headers
+ # The first rule is to prevent including in a frame on a different domain.
+ # Remove it if you want to do that.
+ add_header X-Frame-Options "SAMEORIGIN" always;
+ add_header X-XSS-Protection "1; mode=block" always;
+ add_header X-Content-Type-Options "nosniff" always;
+
+ # Deny access to hidden files
+ location ~ /\. {
+ deny all;
+ access_log off;
+ log_not_found off;
+ }
+
+ # PHP routing - everything goes through index.php
+ location / {
+ # Cache static files
+ # Note that I don't actually serve most of this (just css)
+ # but this prevents requests for static content from getting to the PHP handler.
+ #
+ # I've excluded /css/custom so that requests for uploaded css can be handled by the PHP app.
+ # That lets me store uploaded content outside of the document root,
+ # so it isn't served directly.
+ location ~* ^/(?!css/custom/).+\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
+ expires 1y;
+ add_header Cache-Control "public, immutable";
+ try_files $uri =404;
+ }
+
+ # index.php is the entry point
+ # It needs to be sent to php-fpm
+ # But if someone tries to directly access index.php, that file will throw a 404
+ # so bots and scanners can't tell this is a php app
+ location = /index.php {
+ # CONFIG:
+ # If you're running php-fpm on the same server as nginx,
+ # then change this to the local php-fpm socket
+ # e.g. fastcgi_pass unix:/run/php/php8.2-fpm.sock;
+ fastcgi_pass php:9000;
+
+ # CONFIG:
+ # replace "/var/www/tkr" with the directory you extracted the .zip file to (if different)
+ fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
+ include fastcgi_params;
+
+ fastcgi_param REQUEST_METHOD $request_method;
+ fastcgi_param REQUEST_URI $request_uri;
+ fastcgi_param QUERY_STRING $query_string;
+ }
+
+ # Block attempts to access all other .php files directly
+ # (these are bots and scanners)
+ location ~ ^/.+\.php$ {
+ return 404;
+ }
+
+ # forward other requests to the fallback block,
+ # which sends them to php-fpm for handling
+ try_files $uri $uri/ @tkr_fallback;
+ }
+
+ # Fallback for /tkr routing - all non-file requests (e.g. /login) go to index.php
+ location @tkr_fallback {
+ # CONFIG:
+ # If you're running php-fpm on the same server as nginx,
+ # then change this to the local php-fpm socket
+ # e.g. fastcgi_pass unix:/run/php/php8.2-fpm.sock;
+ fastcgi_pass php:9000;
+
+ # CONFIG:
+ # replace "/var/www/tkr" with the directory you extracted the .zip file to (if different)
+ fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
+ include fastcgi_params;
+
+ fastcgi_param REQUEST_METHOD $request_method;
+ fastcgi_param REQUEST_URI $request_uri;
+ fastcgi_param QUERY_STRING $query_string;
+ }
+
+ # Deny access to sensitive directories
+ location ~ ^/(storage|src|templates|uploads|config) {
+ deny all;
+ return 404;
+ }
+}
+
+server {
+ listen 80 default_server;
+ listen [::]:80 default_server;
+
+ return 301 https://$host$request_uri;
+}
diff --git a/examples/production/nginx/subfolder.conf b/examples/production/nginx/subfolder.conf
new file mode 100644
index 0000000..3095e2b
--- /dev/null
+++ b/examples/production/nginx/subfolder.conf
@@ -0,0 +1,118 @@
+# Example nginx config
+# for serving tkr as a subdfolder with SSL
+# e.g. https://my-domain.com/tkr
+#
+# Use SSL in production.
+# This is a minimal SSL confiuration
+# For more robust SSL configuration, refer to https://ssl-config.mozilla.org/
+server {
+ listen 443 ssl;
+ listen [::]:443 ssl;
+
+ # CONFIG: Replace localhost with your domain e.g. my-domain.com
+ server_name localhost;
+
+ # CONFIG:
+ # Assumes you're using letsencrypt for cert generation
+ # Replace with the actual paths to your cert and key
+ ssl_certificate /etc/letsencrypt/live/my-domain.com/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/my-domain.com/privkey.pem;
+
+ # Security headers
+ # The first rule is to prevent including in a frame on a different domain.
+ # Remove it if you want to do that.
+ add_header X-Frame-Options "SAMEORIGIN" always;
+ add_header X-XSS-Protection "1; mode=block" always;
+ add_header X-Content-Type-Options "nosniff" always;
+
+ # Deny access to hidden files
+ location ~ /\. {
+ deny all;
+ access_log off;
+ log_not_found off;
+ }
+
+ # PHP routing - everything under /tkr goes through index.php
+ location /tkr {
+ # CONFIG:
+ # replace "/var/www/tkr" with the directory you extracted the .zip file to (if different)
+ alias /var/www/tkr/public;
+ index index.php;
+
+ # Cache static files
+ # Note that I don't actually serve most of this (just css)
+ # but this prevents requests for static content from getting to the PHP handler.
+ #
+ # I've excluded /css/custom so that requests for uploaded css can be handled by the PHP app.
+ # That lets me store uploaded content outside of the document root,
+ # so it isn't served directly.
+ location ~* ^/tkr/(?!css/custom/).+\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
+ expires 1y;
+ add_header Cache-Control "public, immutable";
+ try_files $uri =404;
+ }
+
+ # index.php is the entry point
+ # It needs to be sent to php-fpm
+ # But if someone tries to directly access index.php, that file will throw a 404
+ # so bots and scanners can't tell this is a php app
+ location = /tkr/index.php {
+ # CONFIG:
+ # If you're running php-fpm on the same server as nginx,
+ # then change this to the local php-fpm socket
+ # e.g. fastcgi_pass unix:/run/php/php8.2-fpm.sock;
+ fastcgi_pass php:9000;
+
+ # CONFIG:
+ # replace "/var/www/tkr" with the directory you extracted the .zip file to (if different)
+ fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
+ include fastcgi_params;
+
+ fastcgi_param REQUEST_METHOD $request_method;
+ fastcgi_param REQUEST_URI $request_uri;
+ fastcgi_param QUERY_STRING $query_string;
+ }
+
+ # Block attempts to access all other .php files directly
+ # (these are bots and scanners)
+ location ~ ^/tkr/.+\.php$ {
+ return 404;
+ }
+
+ # forward other requests to the fallback block,
+ # which sends them to php-fpm for handling
+ try_files $uri $uri/ @tkr_fallback;
+ }
+
+ # Fallback for /tkr routing - all non-file requests (e.g. /login) go to index.php
+ location @tkr_fallback {
+ # CONFIG:
+ # If you're running php-fpm on the same server as nginx,
+ # then change this to the local php-fpm socket
+ # e.g. fastcgi_pass unix:/run/php/php8.2-fpm.sock;
+ fastcgi_pass php:9000;
+
+ # CONFIG:
+ # replace "/var/www/tkr" with the directory you extracted the .zip file to (if different)
+ fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
+ fastcgi_param SCRIPT_FILENAME /var/www/tkr/public/index.php;
+ include fastcgi_params;
+
+ fastcgi_param REQUEST_METHOD $request_method;
+ fastcgi_param REQUEST_URI $request_uri;
+ fastcgi_param QUERY_STRING $query_string;
+ }
+
+ # Deny access to sensitive directories
+ location ~ ^/tkr/(storage|src|templates|uploads|config) {
+ deny all;
+ return 404;
+ }
+}
+
+server {
+ listen 80 default_server;
+ listen [::]:80 default_server;
+
+ return 301 https://$host$request_uri;
+}
diff --git a/src/Controller/AdminController/AdminController.php b/src/Controller/AdminController/AdminController.php
index 72ada00..91d0fad 100644
--- a/src/Controller/AdminController/AdminController.php
+++ b/src/Controller/AdminController/AdminController.php
@@ -50,7 +50,7 @@ class AdminController extends Controller {
}
$result = $this->saveSettings($_POST, false);
- header('Location: ' . $_SERVER['PHP_SELF']);
+ header('Location: ' . $_SERVER['REQUEST_URI']);
exit;
}
@@ -58,7 +58,7 @@ class AdminController extends Controller {
// for setup, we don't care if they're logged in
// (because they can't be until setup is complete)
$result = $this->saveSettings($_POST, true);
- header('Location: ' . $_SERVER['PHP_SELF']);
+ header('Location: ' . $_SERVER['REQUEST_URI']);
exit;
}
diff --git a/src/Controller/AuthController/AuthController.php b/src/Controller/AuthController/AuthController.php
index 83644ce..58942cb 100644
--- a/src/Controller/AuthController/AuthController.php
+++ b/src/Controller/AuthController/AuthController.php
@@ -39,7 +39,7 @@ class AuthController extends Controller {
} catch (Exception $e) {
Log::error("Failed to create login session for {$username}: " . $e->getMessage());
Session::setFlashMessage('error', 'Login failed - session error');
- header('Location: ' . $_SERVER['PHP_SELF']);
+ header('Location: ' . $_SERVER['REQUEST_URI']);
exit;
}
} else {
@@ -47,13 +47,13 @@ class AuthController extends Controller {
// Set a flash message and reload the login page
Session::setFlashMessage('error', 'Invalid username or password');
- header('Location: ' . $_SERVER['PHP_SELF']);
+ header('Location: ' . $_SERVER['REQUEST_URI']);
exit;
}
} catch (Exception $e) {
Log::error("Database error during login for {$username}: " . $e->getMessage());
Session::setFlashMessage('error', 'Login temporarily unavailable');
- header('Location: ' . $_SERVER['PHP_SELF']);
+ header('Location: ' . $_SERVER['REQUEST_URI']);
exit;
}
}
diff --git a/src/Controller/CssController/CssController.php b/src/Controller/CssController/CssController.php
index 090ffee..cbe8483 100644
--- a/src/Controller/CssController/CssController.php
+++ b/src/Controller/CssController/CssController.php
@@ -20,32 +20,54 @@ class CssController extends Controller {
global $app;
$cssModel = new CssModel($app['db']);
$filename = "$baseFilename.css";
+ Log::debug("Attempting to serve custom css: {$filename}");
+ // Make sure the file exists in the database
$cssRow = $cssModel->getByFilename($filename);
-
if (!$cssRow){
http_response_code(404);
- exit("CSS file not found: $filename");
+ Log::error("Custom css file not in database: {$filename}");
+ exit;
}
+ // Make sure the file exists on the filesystem and is readable
$filePath = CSS_UPLOAD_DIR . "/$filename";
-
if (!file_exists($filePath) || !is_readable($filePath)) {
http_response_code(404);
- exit("CSS file not found: $filePath");
+ Log::error("Custom css file not found or not readable: $filePath");
+ exit;
}
- // This shouldn't be possible, but I'm being extra paranoid
- // about user input
+ // Make sure the file has a .css extension
$ext = strToLower(pathinfo($filename, PATHINFO_EXTENSION));
if($ext != 'css'){
http_response_code(400);
- exit("Invalid file type requested: $ext");
+ Log::error("Invalid file type requested: $ext");
+ exit;
}
+ // If we get here, serve the file
+ Log::debug("Serving custom css: {$filename}");
header('Content-type: text/css');
header('Cache-control: public, max-age=3600');
+ readfile($filePath);
+ exit;
+ }
+ public function serveDefaultCss(){
+ $filePath = PUBLIC_DIR . '/css/default.css';
+ Log::debug("Serving default css: {$filePath}");
+
+ // Make sure the default CSS file exists and is readable
+ if (!file_exists($filePath) || !is_readable($filePath)) {
+ http_response_code(404);
+ Log::error("Default CSS file not found");
+ exit;
+ }
+
+ // Serve the file
+ header('Content-type: text/css');
+ header('Cache-control: public, max-age=3600');
readfile($filePath);
exit;
}
@@ -64,7 +86,7 @@ class CssController extends Controller {
}
// redirect after handling to avoid resubmitting form
- header('Location: ' . $_SERVER['PHP_SELF']);
+ header('Location: ' . $_SERVER['REQUEST_URI']);
exit;
}
@@ -129,7 +151,7 @@ class CssController extends Controller {
try {
if ($_POST['selectCssFile']){
// Set custom theme
- $app['settings']->cssId = $_POST['selectCssFile'];
+ $app['settings']->cssId = (int)($_POST['selectCssFile']);
} else {
// Set default theme
$app['settings']->cssId = null;
diff --git a/src/Framework/Router/Router.php b/src/Framework/Router/Router.php
index 66f5f29..a5d37ce 100644
--- a/src/Framework/Router/Router.php
+++ b/src/Framework/Router/Router.php
@@ -27,6 +27,7 @@ class Router {
['tick/{id}', 'TickController'],
['tick/{id}/delete', 'TickController@handleDelete', ['POST']],
['css/custom/{filename}.css', 'CssController@serveCustomCss'],
+ ['css/default.css', 'CssController@serveDefaultCss'],
];