2025-05-16 20:02:49 -04:00
<? php
2026-04-13 20:56:10 -04:00
2025-05-16 20:02:49 -04:00
// Load environment variables
$envFile = __DIR__ . '/../.env' ;
2026-01-23 21:16:29 -05:00
if ( ! file_exists ( $envFile )) {
die ( 'Configuration error: .env file not found. Copy .env.example to .env and configure your database settings.' );
}
2026-01-01 16:52:35 -05:00
$envVars = parse_ini_file ( $envFile , false , INI_SCANNER_TYPED );
2026-07-10 11:17:07 -04:00
if ( ! is_array ( $envVars )) {
die ( 'Configuration error: .env file could not be parsed. Check for unquoted special characters (e.g. #, ;, =, or quotes) in values and wrap affected values in double quotes.' );
}
2026-01-01 16:52:35 -05:00
// Strip quotes from values if present (parse_ini_file may include them)
if ( $envVars ) {
foreach ( $envVars as $key => $value ) {
if ( is_string ( $value )) {
2026-04-13 20:56:10 -04:00
if (
( substr ( $value , 0 , 1 ) === '"' && substr ( $value , - 1 ) === '"' ) ||
( substr ( $value , 0 , 1 ) === "'" && substr ( $value , - 1 ) === "'" )
) {
2026-01-01 16:52:35 -05:00
$envVars [ $key ] = substr ( $value , 1 , - 1 );
}
}
}
}
2025-05-16 20:02:49 -04:00
// Global configuration
$GLOBALS [ 'config' ] = [
2026-03-29 17:02:40 -04:00
// Application identity
'APP_NAME' => $envVars [ 'APP_NAME' ] ?? 'TINKER TICKETS' ,
'APP_SUBTITLE' => $envVars [ 'APP_SUBTITLE' ] ?? 'LotusGuild Infrastructure' ,
'APP_VERSION' => $envVars [ 'APP_VERSION' ] ?? '1.2' ,
2026-04-01 17:43:18 -04:00
// Asset cache-busting version — auto-computed from key asset mtimes so
2026-03-29 21:09:29 -04:00
// browsers always pick up changes on deploy. Override via ASSET_VERSION in .env.
2026-04-13 20:56:10 -04:00
'ASSET_VERSION' => ( function () use ( $envVars ) {
if ( ! empty ( $envVars [ 'ASSET_VERSION' ])) {
return $envVars [ 'ASSET_VERSION' ];
}
2026-03-29 21:09:29 -04:00
$files = [
2026-04-01 17:43:18 -04:00
__DIR__ . '/../assets/css/base.css' ,
2026-03-29 21:09:29 -04:00
__DIR__ . '/../assets/css/dashboard.css' ,
__DIR__ . '/../assets/css/ticket.css' ,
2026-04-01 17:43:18 -04:00
__DIR__ . '/../assets/js/base.js' ,
2026-03-29 21:09:29 -04:00
__DIR__ . '/../assets/js/dashboard.js' ,
__DIR__ . '/../assets/js/ticket.js' ,
];
$mtime = 0 ;
2026-04-13 20:56:10 -04:00
foreach ( $files as $f ) {
if ( file_exists ( $f )) {
$mtime = max ( $mtime , filemtime ( $f ));
}
}
2026-03-29 21:09:29 -04:00
return $mtime ?: '20260329' ;
})(),
// Canonical ticket statuses — single source of truth used by views and JS
'TICKET_STATUSES' => [ 'Open' , 'Pending' , 'In Progress' , 'Closed' ],
2026-03-29 17:02:40 -04:00
2026-01-20 09:55:01 -05:00
// Database settings
2025-05-16 20:02:49 -04:00
'DB_HOST' => $envVars [ 'DB_HOST' ] ?? 'localhost' ,
'DB_USER' => $envVars [ 'DB_USER' ] ?? 'root' ,
'DB_PASS' => $envVars [ 'DB_PASS' ] ?? '' ,
'DB_NAME' => $envVars [ 'DB_NAME' ] ?? 'tinkertickets' ,
2026-01-20 09:55:01 -05:00
2026-06-30 10:22:53 -04:00
// Trusted reverse proxies. Authelia forward-auth (Remote-* headers) is only
// honored when REMOTE_ADDR is in this allowlist, so the spoofable identity
// headers can't be set by anything that reaches PHP directly. Comma-separated
// IPs in .env (e.g. TRUSTED_PROXIES=10.10.10.27). Empty = enforcement OFF
// (backward compatible — relies solely on network topology).
'TRUSTED_PROXIES' => array_values ( array_filter ( array_map (
'trim' ,
explode ( ',' , ( string )( $envVars [ 'TRUSTED_PROXIES' ] ?? '' ))
), fn ( $ip ) => $ip !== '' )),
2026-01-20 09:55:01 -05:00
// URL settings
2025-09-05 11:08:56 -04:00
'BASE_URL' => '' , // Empty since we're serving from document root
2025-05-16 20:02:49 -04:00
'ASSETS_URL' => '/assets' , // Assets URL
2026-01-20 09:55:01 -05:00
'API_URL' => '/api' , // API URL
2026-02-21 19:17:46 -05:00
// Matrix webhook (hookshot generic webhook URL)
2026-03-29 21:34:16 -04:00
'MATRIX_WEBHOOK_URL' => $envVars [ 'MATRIX_WEBHOOK_URL' ] ?? null ,
// Comma-separated Matrix user IDs to @mention on new tickets / status changes (e.g. @jared:matrix.lotusguild.org)
'MATRIX_NOTIFY_USERS' => $envVars [ 'MATRIX_NOTIFY_USERS' ] ?? '' ,
// Matrix homeserver domain (e.g. matrix.lotusguild.org) — used to construct Matrix user IDs
'MATRIX_DOMAIN' => $envVars [ 'MATRIX_DOMAIN' ] ?? null ,
// Internal Synapse client-API base URL (e.g. http://10.10.10.29:8008) — used to verify user existence via Admin API
'SYNAPSE_ADMIN_URL' => $envVars [ 'SYNAPSE_ADMIN_URL' ] ?? null ,
// Synapse admin access token (generate with: register_new_matrix_user or admin API)
'SYNAPSE_ADMIN_TOKEN' => $envVars [ 'SYNAPSE_ADMIN_TOKEN' ] ?? null ,
// Set to '1' or 'true' to send a notification when any comment is posted
'MATRIX_NOTIFY_COMMENTS' => filter_var ( $envVars [ 'MATRIX_NOTIFY_COMMENTS' ] ?? false , FILTER_VALIDATE_BOOLEAN ),
// Set to '1' or 'true' to send a notification when a ticket is assigned
'MATRIX_NOTIFY_ASSIGNMENTS' => filter_var ( $envVars [ 'MATRIX_NOTIFY_ASSIGNMENTS' ] ?? false , FILTER_VALIDATE_BOOLEAN ),
2026-02-11 14:50:06 -05:00
2026-01-30 18:51:16 -05:00
// Domain settings for external integrations (webhooks, links, etc.)
// Set APP_DOMAIN in .env to override
'APP_DOMAIN' => $envVars [ 'APP_DOMAIN' ] ?? null ,
// Allowed hosts for HTTP_HOST validation (comma-separated in .env)
2026-04-13 20:56:10 -04:00
'ALLOWED_HOSTS' => array_filter ( array_map (
'trim' ,
2026-01-30 18:51:16 -05:00
explode ( ',' , $envVars [ 'ALLOWED_HOSTS' ] ?? 'localhost,127.0.0.1' )
)),
2026-01-20 09:55:01 -05:00
// Session settings
2026-02-11 14:50:06 -05:00
'SESSION_TIMEOUT' => 18000 , // 5 hours in seconds
2026-01-20 09:55:01 -05:00
'SESSION_REGENERATE_INTERVAL' => 300 , // Regenerate session ID every 5 minutes
// CSRF settings
'CSRF_LIFETIME' => 3600 , // 1 hour in seconds
// Pagination settings
'PAGINATION_DEFAULT' => 15 , // Default items per page
'PAGINATION_MAX' => 100 , // Maximum items per page
// File upload settings
'MAX_UPLOAD_SIZE' => 10485760 , // 10MB in bytes
'ALLOWED_FILE_TYPES' => [
'image/jpeg' ,
'image/png' ,
'image/gif' ,
'image/webp' ,
'application/pdf' ,
'text/plain' ,
'text/csv' ,
'application/msword' ,
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' ,
'application/vnd.ms-excel' ,
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ,
'application/zip' ,
'application/x-7z-compressed' ,
'application/x-tar' ,
'application/gzip'
],
'UPLOAD_DIR' => __DIR__ . '/../uploads' ,
// Rate limiting
'RATE_LIMIT_DEFAULT' => 100 , // Requests per minute for general
'RATE_LIMIT_API' => 60 , // Requests per minute for API
// Audit log settings
2026-01-20 21:48:25 -05:00
'AUDIT_LOG_RETENTION_DAYS' => 90 ,
// Timezone settings
// Default: America/New_York (EST/EDT)
// Common options: America/Chicago (CST), America/Denver (MST), America/Los_Angeles (PST), UTC
'TIMEZONE' => $envVars [ 'TIMEZONE' ] ?? 'America/New_York' ,
2026-03-28 20:47:08 -04:00
'TIMEZONE_OFFSET' => null , // Will be calculated below
// LDAP / lldap settings (for user avatar lookups)
'LDAP_HOST' => $envVars [ 'LDAP_HOST' ] ?? '10.10.10.39' ,
'LDAP_PORT' => ( int )( $envVars [ 'LDAP_PORT' ] ?? 3890 ),
'LDAP_BIND_DN' => $envVars [ 'LDAP_BIND_DN' ] ?? 'uid=tinker-tickets,ou=people,dc=example,dc=com' ,
'LDAP_BIND_PW' => $envVars [ 'LDAP_BIND_PW' ] ?? '' ,
'LDAP_BASE_DN' => $envVars [ 'LDAP_BASE_DN' ] ?? 'dc=example,dc=com' ,
'LDAP_USER_BASE' => $envVars [ 'LDAP_USER_BASE' ] ?? 'ou=people,dc=example,dc=com' ,
'LDAP_ENABLED' => filter_var ( $envVars [ 'LDAP_ENABLED' ] ?? 'true' , FILTER_VALIDATE_BOOLEAN ),
'AVATAR_CACHE_DIR' => __DIR__ . '/../uploads/avatars' ,
'AVATAR_CACHE_TTL' => ( int )( $envVars [ 'AVATAR_CACHE_TTL' ] ?? 3600 ), // seconds
2025-09-05 11:08:56 -04:00
];
2026-01-20 21:48:25 -05:00
// Set PHP default timezone
date_default_timezone_set ( $GLOBALS [ 'config' ][ 'TIMEZONE' ]);
// Calculate UTC offset for JavaScript (in minutes, negative for west of UTC)
$now = new DateTime ( 'now' , new DateTimeZone ( $GLOBALS [ 'config' ][ 'TIMEZONE' ]));
$GLOBALS [ 'config' ][ 'TIMEZONE_OFFSET' ] = $now -> getOffset () / 60 ; // Convert seconds to minutes
$GLOBALS [ 'config' ][ 'TIMEZONE_ABBREV' ] = $now -> format ( 'T' ); // e.g., "EST", "EDT"