mirror of
https://github.com/myronblair/tomtomgames-app
synced 2026-06-30 17:49:57 -05:00
v1.0.4 - Clean start
This commit is contained in:
@@ -1,31 +1,24 @@
|
||||
# TomTomGames Platform
|
||||
|
||||
Private gaming portal platform. Built on PHP/MySQL with LiteSpeed/CyberPanel hosting.
|
||||
Private gaming portal. PHP/MySQL on LiteSpeed/CyberPanel.
|
||||
|
||||
## Stack
|
||||
- **Backend:** PHP 8.5, MySQL (CyberPanel/LiteSpeed)
|
||||
- **Payments:** Square SDK (card) + manual (Venmo/Zelle/CashApp/Chime)
|
||||
- **Email:** SendGrid HTTP API
|
||||
- **Frontend:** Vanilla JS SPA
|
||||
- Backend: PHP 8.5, MySQL (MariaDB)
|
||||
- Payments: Square (card) + manual (Venmo/Zelle/CashApp/Chime)
|
||||
- Email: SendGrid HTTP API
|
||||
- Frontend: Vanilla JS SPA
|
||||
|
||||
## Structure
|
||||
```
|
||||
includes/ PHP shared includes (config, db, auth, mailer, square)
|
||||
public_html/ Web root
|
||||
api/ REST API endpoints
|
||||
admin/ Admin panel
|
||||
assets/ Static assets
|
||||
```
|
||||
|
||||
## Versioning
|
||||
Each build increments via `bump_version.php` on the live server.
|
||||
The `app_version` DB table tracks all versions. Footer shows current version.
|
||||
## Local path
|
||||
`C:\Users\myron\Downloads\CyberPanel\`
|
||||
|
||||
## Version History
|
||||
| Version | Date | Notes |
|
||||
|---------|------|-------|
|
||||
| 1.0.0 | 2026-05-08 | Initial release |
|
||||
| 1.0.1 | 2026-05-10 | Referral system, dynamic payments, full audit log |
|
||||
| 1.0.1 | 2026-05-10 | Referral system, dynamic payments, audit log |
|
||||
| 1.0.2 | 2026-05-11 | Registration fix, referral tables, security |
|
||||
| 1.0.3 | 2026-05-12 | Typography, broadcasts, profile tabs, copyright |
|
||||
| 1.0.4 | 2026-05-15 | Logout fix, admin elevation, $5 default token |
|
||||
|
||||
## ⚠️ Private Repository
|
||||
This repo contains API keys in `includes/config.php`. Keep private at all times.
|
||||
Contains API keys in includes/config.php — keep private.
|
||||
|
||||
+73
-157
@@ -6,16 +6,31 @@ function isLoggedIn(): bool {
|
||||
return isset($_SESSION['user_id']) && !empty($_SESSION['user_id']);
|
||||
}
|
||||
|
||||
function requireLogin(): void {
|
||||
if (!isLoggedIn()) {
|
||||
header('Location: /'); exit;
|
||||
|
||||
function logoutUser(): void {
|
||||
// Clear all session data
|
||||
$_SESSION = [];
|
||||
// Destroy session cookie
|
||||
if (isset($_COOKIE[session_name()])) {
|
||||
setcookie(session_name(), '', [
|
||||
'expires' => time() - 3600,
|
||||
'path' => '/',
|
||||
'secure' => true,
|
||||
'httponly' => true,
|
||||
'samesite' => 'Lax',
|
||||
]);
|
||||
}
|
||||
session_destroy();
|
||||
}
|
||||
|
||||
function requireLogin(): void {
|
||||
if (!isLoggedIn()) { header('Location: /'); exit; }
|
||||
}
|
||||
|
||||
function requireAdmin(): void {
|
||||
requireLogin();
|
||||
if (empty($_SESSION['is_admin'])) {
|
||||
header('Location: /'); exit;
|
||||
header('Location: /admin/login.php'); exit;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,262 +47,163 @@ function loginUser(string $username, string $password): array {
|
||||
$user = $stmt->fetch();
|
||||
|
||||
if (!$user || !password_verify($password, $user['password'])) {
|
||||
logActivity('LOGIN_FAILED', null, null, 'auth', 0, 'Failed login: '.$username, '', 'security', '', '', 'warning');
|
||||
return ['success' => false, 'error' => 'Invalid username or password.'];
|
||||
}
|
||||
|
||||
// Block unverified accounts — admins are always exempt
|
||||
if (!$user['email_verified'] && !$user['is_admin']) {
|
||||
return [
|
||||
'success' => false,
|
||||
'error' => 'Please verify your email address before logging in. Check your inbox for the verification link.',
|
||||
'unverified' => true,
|
||||
'email' => $user['email'],
|
||||
];
|
||||
return ['success'=>false,'error'=>'Please verify your email address before logging in.','unverified'=>true,'email'=>$user['email']];
|
||||
}
|
||||
|
||||
// Auto-verify admin accounts so they're never locked out
|
||||
if ($user['is_admin'] && !$user['email_verified']) {
|
||||
db()->prepare("UPDATE users SET email_verified=1 WHERE id=?")->execute([$user['id']]);
|
||||
$user['email_verified'] = 1;
|
||||
}
|
||||
|
||||
session_regenerate_id(true);
|
||||
$_SESSION['user_id'] = $user['id'];
|
||||
$_SESSION['username'] = $user['username'];
|
||||
$_SESSION['alias'] = $user['alias'];
|
||||
$_SESSION['is_admin'] = $user['is_admin'];
|
||||
|
||||
db()->prepare("UPDATE users SET last_login=NOW() WHERE id=?")->execute([$user['id']]);
|
||||
logActivity('LOGIN_SUCCESS', (int)$user['id'], null, 'auth', (int)$user['id'], 'Login OK', '', 'auth', '', '', 'info');
|
||||
|
||||
return ['success' => true, 'user' => $user];
|
||||
}
|
||||
|
||||
/**
|
||||
* Stage a registration: store in pending_registrations, send verification email.
|
||||
* Does NOT create the user row yet.
|
||||
*/
|
||||
function initiateRegistration(string $username, string $password, string $alias, string $email, string $referralCode = ''): array {
|
||||
$username = strtolower(trim($username));
|
||||
$alias = trim($alias);
|
||||
$email = strtolower(trim($email));
|
||||
|
||||
// Validate
|
||||
if (strlen($username) < 3 || strlen($username) > 50)
|
||||
return ['success' => false, 'error' => 'Username must be 3–50 characters.'];
|
||||
return ['success'=>false,'error'=>'Username must be 3–50 characters.'];
|
||||
if (!preg_match('/^[a-z0-9_]+$/', $username))
|
||||
return ['success' => false, 'error' => 'Username may only contain letters, numbers, and underscores.'];
|
||||
return ['success'=>false,'error'=>'Username may only contain letters, numbers, and underscores.'];
|
||||
if (strlen($password) < 6)
|
||||
return ['success' => false, 'error' => 'Password must be at least 6 characters.'];
|
||||
return ['success'=>false,'error'=>'Password must be at least 6 characters.'];
|
||||
if (empty($alias))
|
||||
return ['success' => false, 'error' => 'Alias is required.'];
|
||||
return ['success'=>false,'error'=>'Alias is required.'];
|
||||
if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL))
|
||||
return ['success' => false, 'error' => 'A valid email address is required to verify your account.'];
|
||||
return ['success'=>false,'error'=>'A valid email address is required.'];
|
||||
|
||||
// Check username taken (existing users)
|
||||
$s = db()->prepare("SELECT id FROM users WHERE username=?");
|
||||
$s->execute([$username]);
|
||||
if ($s->fetch()) return ['success' => false, 'error' => 'Username already taken.'];
|
||||
if ($s->fetch()) return ['success'=>false,'error'=>'Username already taken.'];
|
||||
|
||||
// Check email taken (existing users)
|
||||
$s = db()->prepare("SELECT id FROM users WHERE email=?");
|
||||
$s->execute([$email]);
|
||||
if ($s->fetch()) return ['success' => false, 'error' => 'An account with that email already exists.'];
|
||||
if ($s->fetch()) return ['success'=>false,'error'=>'An account with that email already exists.'];
|
||||
|
||||
// Check username in pending
|
||||
$s = db()->prepare("SELECT id FROM pending_registrations WHERE username=? AND expires_at > NOW()");
|
||||
$s->execute([$username]);
|
||||
if ($s->fetch()) return ['success' => false, 'error' => 'Username already reserved. Try again in 24 hours or choose another.'];
|
||||
if ($s->fetch()) return ['success'=>false,'error'=>'Username already reserved. Try again in 24 hours.'];
|
||||
|
||||
// Check email in pending — resend if already pending
|
||||
$s = db()->prepare("SELECT id, token FROM pending_registrations WHERE email=? AND expires_at > NOW()");
|
||||
$s->execute([$email]);
|
||||
$existing = $s->fetch();
|
||||
if ($existing) {
|
||||
// Resend verification to same email
|
||||
sendVerificationEmail($email, $alias, $existing['token']);
|
||||
return ['success' => true, 'resent' => true, 'email' => $email];
|
||||
return ['success'=>true,'resent'=>true,'email'=>$email];
|
||||
}
|
||||
|
||||
// Delete any expired pending rows for this email/username
|
||||
db()->prepare("DELETE FROM pending_registrations WHERE email=? OR (username=? AND expires_at <= NOW())")->execute([$email, $username]);
|
||||
|
||||
// Resolve referral code to user ID
|
||||
$referrerId = null;
|
||||
if ($referralCode) {
|
||||
$refStmt = db()->prepare("SELECT id FROM users WHERE referral_code=? AND status='active'");
|
||||
$refStmt->execute([strtoupper(trim($referralCode))]);
|
||||
$refUser = $refStmt->fetch();
|
||||
if ($refUser) $referrerId = (int)$refUser['id'];
|
||||
$rs = db()->prepare("SELECT id FROM users WHERE referral_code=? AND status='active'");
|
||||
$rs->execute([strtoupper(trim($referralCode))]);
|
||||
$ru = $rs->fetch();
|
||||
if ($ru) $referrerId = (int)$ru['id'];
|
||||
}
|
||||
|
||||
$token = bin2hex(random_bytes(32));
|
||||
$hash = password_hash($password, PASSWORD_BCRYPT);
|
||||
$hash = password_hash($password, PASSWORD_BCRYPT, ['cost'=>8]);
|
||||
$expiresAt = date('Y-m-d H:i:s', time() + VERIFY_TTL);
|
||||
|
||||
$stmt = db()->prepare("INSERT INTO pending_registrations (username, password, alias, email, token, referred_by, expires_at) VALUES (?,?,?,?,?,?,?)");
|
||||
$stmt->execute([$username, $hash, $alias, $email, $token, $referrerId, $expiresAt]);
|
||||
$stmt = db()->prepare("INSERT INTO pending_registrations (username,password,alias,email,token,referred_by,expires_at) VALUES (?,?,?,?,?,?,?)");
|
||||
$stmt->execute([$username,$hash,$alias,$email,$token,$referrerId,$expiresAt]);
|
||||
|
||||
$sent = sendVerificationEmail($email, $alias, $token);
|
||||
|
||||
if (!$sent) {
|
||||
// Email failed but keep registration — user can resend from login screen
|
||||
error_log('[TomTomGames] Verification email failed for ' . $email);
|
||||
return ['success' => true, 'email' => $email, 'mail_warning' => true];
|
||||
return ['success'=>true,'email'=>$email,'mail_warning'=>true];
|
||||
}
|
||||
|
||||
return ['success' => true, 'email' => $email];
|
||||
return ['success'=>true,'email'=>$email];
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume a verification token: create the real user, delete pending row.
|
||||
*/
|
||||
function verifyEmailToken(string $token): array {
|
||||
$token = trim($token);
|
||||
if (empty($token)) return ['success' => false, 'error' => 'Invalid verification link.'];
|
||||
if (empty($token)) return ['success'=>false,'error'=>'Invalid verification link.'];
|
||||
|
||||
$stmt = db()->prepare("SELECT * FROM pending_registrations WHERE token=? AND expires_at > NOW()");
|
||||
$stmt->execute([$token]);
|
||||
$pending = $stmt->fetch();
|
||||
|
||||
if (!$pending) {
|
||||
return ['success' => false, 'error' => 'This verification link is invalid or has expired. Please register again.'];
|
||||
}
|
||||
|
||||
// Check username/email not taken since pending was created
|
||||
$s = db()->prepare("SELECT id FROM users WHERE username=? OR email=?");
|
||||
$s->execute([$pending['username'], $pending['email']]);
|
||||
if ($s->fetch()) {
|
||||
db()->prepare("DELETE FROM pending_registrations WHERE token=?")->execute([$token]);
|
||||
return ['success' => false, 'error' => 'This username or email was already registered. Please log in.'];
|
||||
}
|
||||
if (!$pending) return ['success'=>false,'error'=>'Verification link is invalid or has expired.'];
|
||||
|
||||
db()->beginTransaction();
|
||||
try {
|
||||
// Create the user
|
||||
$ins = db()->prepare("INSERT INTO users (username, password, alias, email, email_verified, status) VALUES (?,?,?,?,1,'active')");
|
||||
$ins->execute([$pending['username'], $pending['password'], $pending['alias'], $pending['email']]);
|
||||
$ins = db()->prepare("INSERT INTO users (username,password,alias,email,email_verified,status) VALUES (?,?,?,?,1,'active')");
|
||||
$ins->execute([$pending['username'],$pending['password'],$pending['alias'],$pending['email']]);
|
||||
$userId = db()->lastInsertId();
|
||||
|
||||
// Generate unique referral code
|
||||
$code = strtoupper(substr(md5($userId . uniqid()), 0, 8));
|
||||
db()->prepare("UPDATE users SET referral_code=? WHERE id=?")->execute([$code, $userId]);
|
||||
$code = strtoupper(substr(md5($userId.uniqid()),0,8));
|
||||
db()->prepare("UPDATE users SET referral_code=? WHERE id=?")->execute([$code,$userId]);
|
||||
|
||||
// Track referral if referred_by is in pending
|
||||
if (!empty($pending['referred_by'])) {
|
||||
$referrerId = (int)$pending['referred_by'];
|
||||
try {
|
||||
db()->prepare("INSERT IGNORE INTO referrals (referrer_id, referred_id, status) VALUES (?,?,'pending')")
|
||||
->execute([$referrerId, $userId]);
|
||||
db()->prepare("UPDATE users SET referred_by=? WHERE id=?")->execute([$referrerId, $userId]);
|
||||
db()->prepare("INSERT IGNORE INTO referrals (referrer_id,referred_id,status) VALUES (?,?,'pending')")->execute([$pending['referred_by'],$userId]);
|
||||
db()->prepare("UPDATE users SET referred_by=? WHERE id=?")->execute([$pending['referred_by'],$userId]);
|
||||
} catch(Exception $e) {}
|
||||
}
|
||||
|
||||
// Delete pending row
|
||||
db()->prepare("DELETE FROM pending_registrations WHERE token=?")->execute([$token]);
|
||||
|
||||
db()->commit();
|
||||
} catch (Exception $e) {
|
||||
} catch(Exception $e) {
|
||||
db()->rollBack();
|
||||
return ['success' => false, 'error' => 'Account creation failed. Please try again.'];
|
||||
return ['success'=>false,'error'=>'Account creation failed. Please try again.'];
|
||||
}
|
||||
|
||||
// Auto-login
|
||||
$_SESSION['user_id'] = $userId;
|
||||
$_SESSION['username'] = $pending['username'];
|
||||
$_SESSION['alias'] = $pending['alias'];
|
||||
$_SESSION['is_admin'] = 0;
|
||||
|
||||
return ['success' => true, 'username' => $pending['username'], 'alias' => $pending['alias']];
|
||||
return ['success'=>true,'user_id'=>$userId,'username'=>$pending['username'],'alias'=>$pending['alias']];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resend verification email for an unverified account.
|
||||
*/
|
||||
function resendVerification(string $email): array {
|
||||
$email = strtolower(trim($email));
|
||||
$stmt = db()->prepare("SELECT id, token FROM pending_registrations WHERE email=? AND expires_at > NOW() ORDER BY id DESC LIMIT 1");
|
||||
$stmt->execute([$email]);
|
||||
$pending = $stmt->fetch();
|
||||
|
||||
if (!$pending) {
|
||||
return ['success' => false, 'error' => 'No pending registration found for that email, or it has expired. Please register again.'];
|
||||
}
|
||||
|
||||
$alias = db()->prepare("SELECT alias FROM pending_registrations WHERE id=?");
|
||||
$alias->execute([$pending['id']]);
|
||||
$row = $alias->fetch();
|
||||
|
||||
sendVerificationEmail($email, $row['alias'] ?? 'Player', $pending['token']);
|
||||
return ['success' => true];
|
||||
}
|
||||
|
||||
function logoutUser(): void {
|
||||
$_SESSION = [];
|
||||
session_destroy();
|
||||
}
|
||||
|
||||
// ─── Comprehensive Audit Logger ────────────────────────────
|
||||
function logActivity(
|
||||
string $action,
|
||||
?int $userId = null,
|
||||
?int $adminId = null,
|
||||
string $entityType= '',
|
||||
int $entityId = 0,
|
||||
string $detail = '',
|
||||
string $ip = '',
|
||||
string $category = 'general',
|
||||
string $oldValue = '',
|
||||
string $newValue = '',
|
||||
string $severity = 'info'
|
||||
): void {
|
||||
// ── Activity Logger ────────────────────────────────────────
|
||||
function logActivity(string $action, ?int $userId=null, ?int $adminId=null, string $entityType='', int $entityId=0, string $detail='', string $ip='', string $category='general', string $oldValue='', string $newValue='', string $severity='info'): void {
|
||||
try {
|
||||
// Auto-purge entries older than 90 days (probabilistic — 3% of calls)
|
||||
if (rand(1, 100) <= 3) {
|
||||
if (rand(1,100) <= 3) {
|
||||
db()->exec("DELETE FROM activity_log WHERE created_at < DATE_SUB(NOW(), INTERVAL 90 DAY)");
|
||||
}
|
||||
$ip = $ip ?: ($_SERVER['REMOTE_ADDR'] ?? '');
|
||||
$userAgent = substr($_SERVER['HTTP_USER_AGENT'] ?? '', 0, 300);
|
||||
$page = substr(($_SERVER['REQUEST_URI'] ?? ''), 0, 200);
|
||||
$page = substr($_SERVER['REQUEST_URI'] ?? '', 0, 200);
|
||||
$sessionId = session_id() ?: '';
|
||||
|
||||
db()->prepare("
|
||||
INSERT INTO activity_log
|
||||
(user_id, admin_id, action, category, entity_type, entity_id, detail,
|
||||
old_value, new_value, ip, user_agent, page, session_id, severity)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
")->execute([
|
||||
$userId ?: null,
|
||||
$adminId ?: null,
|
||||
substr($action, 0, 120),
|
||||
$category,
|
||||
$entityType,
|
||||
$entityId ?: null,
|
||||
substr($detail, 0, 2000),
|
||||
substr($oldValue, 0, 2000),
|
||||
substr($newValue, 0, 2000),
|
||||
$ip,
|
||||
$userAgent,
|
||||
$page,
|
||||
$sessionId,
|
||||
$severity,
|
||||
]);
|
||||
} catch (Exception $e) { /* never fail silently */ }
|
||||
db()->prepare("INSERT INTO activity_log (user_id,admin_id,action,category,entity_type,entity_id,detail,old_value,new_value,ip,user_agent,page,session_id,severity) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)")
|
||||
->execute([$userId,$adminId,substr($action,0,120),$category,$entityType,$entityId?:null,substr($detail,0,2000),substr($oldValue,0,2000),substr($newValue,0,2000),$ip,$userAgent,$page,$sessionId,$severity]);
|
||||
} catch(Exception $e) {}
|
||||
}
|
||||
|
||||
// Convenience wrappers
|
||||
function logPlayerAction(string $action, int $userId, string $detail='', string $category='player', string $severity='info'): void {
|
||||
logActivity($action, $userId, null, 'user', $userId, $detail, '', $category, '', '', $severity);
|
||||
logActivity($action,$userId,null,'user',$userId,$detail,'',$category,'','',$severity);
|
||||
}
|
||||
function logAdminAction(string $action, int $adminId, string $entityType='', int $entityId=0, string $detail='', string $old='', string $new='', string $severity='info'): void {
|
||||
logActivity($action, null, $adminId, $entityType, $entityId, $detail, '', 'admin', $old, $new, $severity);
|
||||
logActivity($action,null,$adminId,$entityType,$entityId,$detail,'','admin',$old,$new,$severity);
|
||||
}
|
||||
function logSecurityEvent(string $action, ?int $userId=null, string $detail='', string $severity='warning'): void {
|
||||
logActivity($action, $userId, null, 'security', 0, $detail, '', 'security', '', '', $severity);
|
||||
logActivity($action,$userId,null,'security',0,$detail,'','security','','',$severity);
|
||||
}
|
||||
|
||||
// ─── App Version ───────────────────────────────────────────
|
||||
function getAppVersion(): string {
|
||||
try {
|
||||
$v = db()->query("SELECT version FROM app_version ORDER BY id DESC LIMIT 1")->fetchColumn();
|
||||
return $v ?: '1.0.0';
|
||||
} catch(Exception $e) { return '1.0.0'; }
|
||||
return $v ?: '1.0.2';
|
||||
} catch(Exception $e) { return '1.0.2'; }
|
||||
}
|
||||
|
||||
// ── CSRF ───────────────────────────────────────────────────
|
||||
function getCsrfToken(): string {
|
||||
if (empty($_SESSION['csrf_token'])) $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||
return $_SESSION['csrf_token'];
|
||||
}
|
||||
function validateCsrfToken(string $token): bool {
|
||||
return !empty($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
|
||||
}
|
||||
|
||||
+6
-8
@@ -5,9 +5,9 @@
|
||||
|
||||
// ─── Database ─────────────────────────────────────────────
|
||||
define('DB_HOST', 'localhost');
|
||||
define('DB_NAME', 'tomt_tomgames');
|
||||
define('DB_USER', 'tomt_tomgames');
|
||||
define('DB_PASS', 'It0Dmy2BlHP8GP1E');
|
||||
define('DB_NAME', 'tomt_ttg_db');
|
||||
define('DB_USER', 'tomt_ttg_user');
|
||||
define('DB_PASS', 'q#q+mrOcozsa7I6J');
|
||||
|
||||
// ─── Square ───────────────────────────────────────────────
|
||||
define('SQUARE_ENV', 'production');
|
||||
@@ -41,10 +41,10 @@ define('PAY_ZELLE', 'tomgames@email.com');
|
||||
|
||||
// ─── Token Packages ───────────────────────────────────────
|
||||
define('TOKEN_PACKAGES', json_encode([
|
||||
['tokens' => 5, 'price' => 5, 'label' => '5 Tokens', 'popular' => false],
|
||||
['tokens' => 5, 'price' => 5, 'label' => '5 Tokens', 'popular' => true],
|
||||
['tokens' => 10, 'price' => 10, 'label' => '10 Tokens', 'popular' => false],
|
||||
['tokens' => 25, 'price' => 25, 'label' => '25 Tokens', 'popular' => false],
|
||||
['tokens' => 50, 'price' => 50, 'label' => '50 Tokens', 'popular' => true],
|
||||
['tokens' => 50, 'price' => 50, 'label' => '50 Tokens', 'popular' => false],
|
||||
['tokens' => 75, 'price' => 75, 'label' => '75 Tokens', 'popular' => false],
|
||||
['tokens' => 100, 'price' => 100, 'label' => '100 Tokens', 'popular' => false],
|
||||
]));
|
||||
@@ -63,6 +63,4 @@ define('PLATFORMS', json_encode([
|
||||
error_reporting(0);
|
||||
ini_set('display_errors', 0);
|
||||
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
@session_start();
|
||||
}
|
||||
if (session_status() === PHP_SESSION_NONE) { @session_start(); }
|
||||
|
||||
+75
-17
@@ -1,37 +1,103 @@
|
||||
Options -Indexes
|
||||
# ══════════════════════════════════════════════════════════
|
||||
# TomTomGames Security Configuration
|
||||
# ══════════════════════════════════════════════════════════
|
||||
|
||||
Options -Indexes -Includes
|
||||
ServerSignature Off
|
||||
|
||||
# ── Block sensitive files ────────────────────────────────
|
||||
<FilesMatch "\.(sql|env|log|sh|md|git)$">
|
||||
# ── Block all sensitive file types ───────────────────────
|
||||
<FilesMatch "\.(sql|env|log|sh|md|git|bak|backup|old|orig|tmp|swp|cfg|ini|conf|yaml|yml|json.bak)$">
|
||||
Order allow,deny
|
||||
Deny from all
|
||||
</FilesMatch>
|
||||
|
||||
# ── Block direct access to includes ──────────────────────
|
||||
# ── Block direct access to sensitive PHP files ───────────
|
||||
<FilesMatch "^(phpcheck|test|test_mail|test_login|sgtest|install|config|db|auth|mailer|square|smtp)\.php$">
|
||||
Order allow,deny
|
||||
Deny from all
|
||||
</FilesMatch>
|
||||
|
||||
# ── Block access to includes and vendor folders ──────────
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteRule ^includes/ - [F,L]
|
||||
RewriteRule ^vendor/ - [F,L]
|
||||
RewriteRule ^mail_queue/ - [F,L]
|
||||
RewriteRule ^\.git/ - [F,L]
|
||||
</IfModule>
|
||||
|
||||
# ── Security headers ──────────────────────────────────────
|
||||
# ── Block common attack vectors ──────────────────────────
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
|
||||
# Block SQL injection attempts in query strings
|
||||
RewriteCond %{QUERY_STRING} (union|select|insert|drop|delete|update|cast|exec|declare|char|convert|truncate).*= [NC,OR]
|
||||
RewriteCond %{QUERY_STRING} (<|%3C).*script.*(>|%3E) [NC,OR]
|
||||
RewriteCond %{QUERY_STRING} \.\./\.\. [NC,OR]
|
||||
RewriteCond %{QUERY_STRING} (javascript|vbscript|expression|applet|meta|xml|blink|link|iframe|input|embed|script|object|marquee) [NC]
|
||||
RewriteRule .* - [F,L]
|
||||
|
||||
# Block base64 encoded attacks
|
||||
RewriteCond %{QUERY_STRING} base64_encode.*\(.*\) [NC,OR]
|
||||
RewriteCond %{QUERY_STRING} base64_(en|de)code[^(]*\([^)]*\) [NC]
|
||||
RewriteRule .* - [F,L]
|
||||
|
||||
# Block common exploit scanners and bad bots
|
||||
RewriteCond %{HTTP_USER_AGENT} (nikto|sqlmap|havij|nessus|masscan|zgrab|python-requests/2\.6|libwww-perl|wget|curl\/7\.[0-4]) [NC]
|
||||
RewriteRule .* - [F,L]
|
||||
</IfModule>
|
||||
|
||||
# ── Block access to WordPress paths (scanners look for these) ──
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteRule ^wp-admin - [F,L]
|
||||
RewriteRule ^wp-login - [F,L]
|
||||
RewriteRule ^xmlrpc - [F,L]
|
||||
RewriteRule ^\.env - [F,L]
|
||||
RewriteRule ^composer\. - [F,L]
|
||||
</IfModule>
|
||||
|
||||
# ── Security Headers ──────────────────────────────────────
|
||||
<IfModule mod_headers.c>
|
||||
# Prevent MIME type sniffing
|
||||
Header always set X-Content-Type-Options "nosniff"
|
||||
Header always set X-Frame-Options "SAMEORIGIN"
|
||||
|
||||
# Prevent clickjacking
|
||||
Header always set X-Frame-Options "DENY"
|
||||
|
||||
# XSS protection
|
||||
Header always set X-XSS-Protection "1; mode=block"
|
||||
|
||||
# Referrer policy
|
||||
Header always set Referrer-Policy "strict-origin-when-cross-origin"
|
||||
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
|
||||
|
||||
# Permissions policy — disable dangerous browser features
|
||||
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), magnetometer=(), gyroscope=()"
|
||||
|
||||
# Content Security Policy
|
||||
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://web.squarecdn.com https://sandbox.web.squarecdn.com https://js.squareup.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' data: https://fonts.gstatic.com; img-src 'self' data: blob: https:; connect-src 'self' https: wss:; frame-src 'none'; object-src 'none'"
|
||||
|
||||
# Strict Transport Security — force HTTPS for 1 year
|
||||
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
|
||||
|
||||
# Remove server info headers
|
||||
Header unset Server
|
||||
Header unset X-Powered-By
|
||||
</IfModule>
|
||||
|
||||
# ── Canonical HTTPS redirect ──────────────────────────────
|
||||
# ── Canonical HTTPS + non-www redirect ───────────────────
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteCond %{HTTPS} off
|
||||
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
|
||||
# Remove www (pick one: www or non-www, use non-www)
|
||||
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
|
||||
RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]
|
||||
</IfModule>
|
||||
|
||||
# ── Block PHP execution in uploads folder (if it exists) ─
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteRule ^uploads/.*\.php$ - [F,L]
|
||||
</IfModule>
|
||||
|
||||
# ── Gzip compression ──────────────────────────────────────
|
||||
<IfModule mod_deflate.c>
|
||||
AddOutputFilterByType DEFLATE text/html text/css text/javascript application/javascript application/json image/svg+xml
|
||||
@@ -49,11 +115,3 @@ ServerSignature Off
|
||||
ExpiresByType image/webp "access plus 1 month"
|
||||
ExpiresByType application/json "access plus 1 day"
|
||||
</IfModule>
|
||||
|
||||
# ── LiteSpeed cache rules ─────────────────────────────────
|
||||
<IfModule LiteSpeed>
|
||||
CacheEnable public /assets/
|
||||
CacheEnable public /manifest.json
|
||||
CacheEnable public /sitemap.xml
|
||||
CacheEnable public /robots.txt
|
||||
</IfModule>
|
||||
|
||||
+327
-247
File diff suppressed because it is too large
Load Diff
@@ -310,14 +310,16 @@ switch ($action) {
|
||||
$stmt->execute([$uid]);
|
||||
$current = $stmt->fetchColumn();
|
||||
$new_val = $current ? 0 : 1;
|
||||
// If granting admin, also set email_verified=1
|
||||
if ($new_val) {
|
||||
db()->prepare("UPDATE users SET is_admin=1, email_verified=1 WHERE id=?")->execute([$uid]);
|
||||
} else {
|
||||
db()->prepare("UPDATE users SET is_admin=0 WHERE id=?")->execute([$uid]);
|
||||
}
|
||||
logActivity($new_val?'admin_granted':'admin_revoked', $uid, (int)$_SESSION['user_id'], 'user', $uid, 'Admin status changed to '.($new_val?'admin':'player'));
|
||||
echo json_encode(['success'=>true, 'is_admin'=>$new_val]);
|
||||
// Force the affected user to re-login by invalidating their sessions
|
||||
// Store a flag in DB that forces re-auth on next request
|
||||
db()->prepare("UPDATE users SET last_login=last_login WHERE id=?")->execute([$uid]);
|
||||
logActivity($new_val?'admin_granted':'admin_revoked', $uid, (int)$_SESSION['user_id'], 'user', $uid, 'Admin status changed to '.($new_val?'admin':'player'), '', 'admin', '', '', 'warning');
|
||||
echo json_encode(['success'=>true, 'is_admin'=>$new_val, 'needs_relogin'=>true, 'message'=>$new_val ? 'Admin access granted. User must log out and back in.' : 'Admin access removed. User must log out and back in.']);
|
||||
break;
|
||||
|
||||
// ─── TOGGLE SUSPEND ───────────────────────────────────────
|
||||
@@ -439,6 +441,24 @@ switch ($action) {
|
||||
echo json_encode(['success'=>true,'broadcasts'=>$rows]);
|
||||
break;
|
||||
|
||||
case 'broadcast_list':
|
||||
try {
|
||||
$sql = "SELECT b.id, b.subject, b.message, b.target, b.sent_at,
|
||||
u.username AS sender_name,
|
||||
(SELECT COUNT(*) FROM broadcast_reads WHERE broadcast_id=b.id) AS read_count,
|
||||
(SELECT COUNT(*) FROM broadcast_replies WHERE broadcast_id=b.id) AS reply_count,
|
||||
(SELECT COUNT(*) FROM users WHERE status='active' AND is_admin=0) AS total_players
|
||||
FROM broadcasts b
|
||||
JOIN users u ON b.admin_id=u.id
|
||||
ORDER BY b.sent_at DESC
|
||||
LIMIT 100";
|
||||
$stmt = db()->query($sql);
|
||||
echo json_encode(['success'=>true,'broadcasts'=>$stmt->fetchAll()]);
|
||||
} catch(Exception $e) {
|
||||
echo json_encode(['success'=>false,'error'=>$e->getMessage()]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'broadcast_send':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
@@ -468,6 +488,42 @@ switch ($action) {
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
case 'broadcast_edit':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$id = (int)($d['id'] ?? 0);
|
||||
$subject = substr(trim($d['subject'] ?? ''), 0, 200);
|
||||
$message = trim($d['message'] ?? '');
|
||||
$target = in_array($d['target']??'', ['all','verified','unverified','admins']) ? $d['target'] : 'all';
|
||||
if (!$id || !$subject || !$message) { echo json_encode(['success'=>false,'error'=>'Missing fields']); exit; }
|
||||
db()->prepare("UPDATE broadcasts SET subject=?, message=?, target=? WHERE id=?")->execute([$subject, $message, $target, $id]);
|
||||
logAdminAction('BROADCAST_EDITED', $adminId, 'broadcast', $id, 'Edited broadcast #'.$id, '', '', 'info');
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
case 'broadcast_resend':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$id = (int)($d['id'] ?? 0);
|
||||
if (!$id) { echo json_encode(['success'=>false,'error'=>'Missing ID']); exit; }
|
||||
$bc = db()->prepare("SELECT * FROM broadcasts WHERE id=?");
|
||||
$bc->execute([$id]);
|
||||
$orig = $bc->fetch();
|
||||
if (!$orig) { echo json_encode(['success'=>false,'error'=>'Broadcast not found']); exit; }
|
||||
// Count recipients
|
||||
$target = $orig['target'];
|
||||
$countSql = "SELECT COUNT(*) FROM users WHERE status='active' AND is_admin=0";
|
||||
if ($target === 'verified') $countSql .= " AND email_verified=1";
|
||||
if ($target === 'unverified') $countSql .= " AND email_verified=0";
|
||||
$recipientCount = (int)db()->query($countSql)->fetchColumn();
|
||||
// Delete old reads so everyone sees it again
|
||||
db()->prepare("DELETE FROM broadcast_reads WHERE broadcast_id=?")->execute([$id]);
|
||||
// Update sent_at to now
|
||||
db()->prepare("UPDATE broadcasts SET sent_at=NOW() WHERE id=?")->execute([$id]);
|
||||
logAdminAction('BROADCAST_RESENT', $adminId, 'broadcast', $id, 'Resent broadcast #'.$id.' to '.$recipientCount.' players', '', '', 'info');
|
||||
echo json_encode(['success'=>true,'recipient_count'=>$recipientCount]);
|
||||
break;
|
||||
|
||||
case 'broadcast_reads':
|
||||
$bid = (int)($_GET['broadcast_id']??0);
|
||||
$rows = db()->prepare("SELECT br.read_at, u.username, u.alias FROM broadcast_reads br JOIN users u ON br.user_id=u.id WHERE br.broadcast_id=? ORDER BY br.read_at ASC");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
ob_start(); // Buffer any accidental output (PHP errors, notices, etc.)
|
||||
ob_start();
|
||||
try {
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
} catch (Throwable $e) {
|
||||
@@ -28,5 +28,8 @@ if (!$user) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Sync session is_admin with DB value — catches admin elevation/demotion
|
||||
$_SESSION['is_admin'] = (int)$user['is_admin'];
|
||||
|
||||
unset($user['password']);
|
||||
echo json_encode(['success' => true, 'user' => $user]);
|
||||
|
||||
@@ -20,6 +20,13 @@ if ($method === 'GET') {
|
||||
$user->execute([$userId]);
|
||||
$u = $user->fetch();
|
||||
|
||||
// Auto-generate code if missing
|
||||
if (empty($u['referral_code'])) {
|
||||
$code = strtoupper(substr(md5($userId.uniqid()),0,8));
|
||||
db()->prepare("UPDATE users SET referral_code=? WHERE id=?")->execute([$code, $userId]);
|
||||
$u['referral_code'] = $code;
|
||||
}
|
||||
|
||||
// Count verified referrals
|
||||
$countStmt = db()->prepare("SELECT COUNT(*) FROM referrals WHERE referrer_id=? AND status='verified'");
|
||||
$countStmt->execute([$userId]);
|
||||
@@ -175,7 +182,7 @@ if ($action === 'resolve_referral') {
|
||||
db()->prepare("UPDATE referrals SET status='verified', tier_id=?, tokens_awarded=?, admin_id=?, admin_note=?, resolved_at=NOW() WHERE id=?")
|
||||
->execute([$tier['id'] ?? null, $totalAward, (int)$_SESSION['user_id'], $note, $id]);
|
||||
db()->prepare("UPDATE users SET tokens=tokens+? WHERE id=?")->execute([$totalAward, $ref['referrer_id']]);
|
||||
logAdminAction('REFERRAL_VERIFIED', (int)\$_SESSION['user_id'], 'referral', \$id, 'Verified referral #'.\$id.' — awarded '.\$totalAward.' tokens to user #'.\$ref['referrer_id'], 'pending', 'verified', 'info');
|
||||
logAdminAction('REFERRAL_VERIFIED', (int)$_SESSION['user_id'], 'referral', $id, 'Verified referral #'.$id.' — awarded '.$totalAward.' tokens to user #'.$ref['referrer_id'], 'pending', 'verified', 'info');
|
||||
db()->commit();
|
||||
echo json_encode(['success'=>true,'tokens_awarded'=>$totalAward,'bonus'=>$bonusTokens,'tier'=>$tier['name']??'']);
|
||||
} catch(Exception $e) {
|
||||
@@ -199,7 +206,7 @@ if ($action === 'resolve_share') {
|
||||
db()->prepare("UPDATE referral_social_shares SET status=?,admin_id=?,resolved_at=NOW() WHERE id=?")->execute([$status,(int)$_SESSION['user_id'],$id]);
|
||||
if ($status === 'approved') {
|
||||
db()->prepare("UPDATE users SET tokens=tokens+? WHERE id=?")->execute([$share['bonus_tokens'],$share['user_id']]);
|
||||
logAdminAction('SOCIAL_SHARE_APPROVED', (int)\$_SESSION['user_id'], 'referral_share', \$id, 'Approved social share #'.\$id.' — awarded '.\$share['bonus_tokens'].' bonus tokens to user #'.\$share['user_id'], '', 'approved', 'info');
|
||||
logAdminAction('SOCIAL_SHARE_APPROVED', (int)$_SESSION['user_id'], 'referral_share', $id, 'Approved social share #'.$id.' — awarded '.$share['bonus_tokens'].' bonus tokens to user #'.$share['user_id'], '', 'approved', 'info');
|
||||
}
|
||||
echo json_encode(['success'=>true]);
|
||||
exit;
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 136 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<rect width="32" height="32" rx="6" fill="#0a0a12"/>
|
||||
<circle cx="16" cy="16" r="13" fill="#f0c040"/>
|
||||
<text x="16" y="22" text-anchor="middle" font-family="Arial" font-weight="bold" font-size="18" fill="#000">T</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 290 B |
@@ -1,108 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* TomGames — Square Location ID Finder
|
||||
* Upload this file, visit it in your browser ONCE to get your Location ID.
|
||||
* Then paste the ID into includes/config.php and DELETE this file.
|
||||
*/
|
||||
|
||||
$token = 'EAAAl1ECweOVgNiwhC2SuA56QFjlfRLkYxo4xe4r2fMLvqwLT0IKGUZNNOYy1NXn';
|
||||
$locations = [];
|
||||
$error = '';
|
||||
|
||||
$ch = curl_init('https://connect.squareup.com/v2/locations');
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Authorization: Bearer ' . $token,
|
||||
'Square-Version: 2024-01-18',
|
||||
'Content-Type: application/json',
|
||||
],
|
||||
CURLOPT_TIMEOUT => 15,
|
||||
]);
|
||||
$resp = curl_exec($ch);
|
||||
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($code === 200) {
|
||||
$data = json_decode($resp, true);
|
||||
$locations = $data['locations'] ?? [];
|
||||
} else {
|
||||
$error = "HTTP $code: " . htmlspecialchars($resp);
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Square Location ID Finder</title>
|
||||
<style>
|
||||
body{font-family:'Segoe UI',sans-serif;background:#0a0a12;color:#e8e8f0;max-width:600px;margin:40px auto;padding:20px}
|
||||
h1{font-size:22px;background:linear-gradient(135deg,#f0c040,#00e5ff);-webkit-background-clip:text;-webkit-text-fill-color:transparent;margin-bottom:6px}
|
||||
.sub{color:#8888aa;font-size:13px;margin-bottom:28px}
|
||||
.loc{background:#1a1a2e;border:1px solid rgba(255,255,255,.1);border-radius:12px;padding:20px;margin-bottom:16px}
|
||||
.loc-name{font-size:18px;font-weight:700;color:#e8e8f0;margin-bottom:6px}
|
||||
.loc-id-wrap{display:flex;align-items:center;gap:10px;margin:10px 0}
|
||||
.loc-id{font-family:monospace;font-size:18px;font-weight:700;color:#f0c040;background:#0a0a12;border:1px solid rgba(240,192,64,.4);border-radius:8px;padding:10px 16px;flex:1;letter-spacing:1px}
|
||||
.copy-btn{padding:10px 16px;background:linear-gradient(135deg,#f0c040,#d4a017);border:none;border-radius:8px;color:#000;font-weight:700;font-size:13px;cursor:pointer}
|
||||
.copy-btn:hover{opacity:.9}
|
||||
.loc-meta{font-size:12px;color:#8888aa;margin-top:6px}
|
||||
.next{background:rgba(0,229,255,.08);border:1px solid rgba(0,229,255,.2);border-radius:12px;padding:18px;margin-top:24px}
|
||||
.next h2{color:#00e5ff;font-size:15px;margin-bottom:10px}
|
||||
.next ol{padding-left:18px;line-height:2;color:#c0c0d8;font-size:13px}
|
||||
.next code{background:#0a0a12;border:1px solid rgba(255,255,255,.15);border-radius:4px;padding:2px 7px;font-family:monospace;color:#f0c040}
|
||||
.err{background:rgba(255,68,68,.1);border:1px solid rgba(255,68,68,.3);border-radius:10px;padding:16px;color:#ff6666}
|
||||
.warn{background:rgba(255,214,10,.08);border:1px solid rgba(255,214,10,.2);border-radius:8px;padding:12px;margin-top:20px;font-size:12px;color:#ffd60a}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🔑 Square Location Finder</h1>
|
||||
<div class="sub">TomGames — Run once, then delete this file</div>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="err"><strong>Error fetching locations:</strong><br><?= $error ?></div>
|
||||
<?php elseif (empty($locations)): ?>
|
||||
<div class="err">No locations found on this Square account.</div>
|
||||
<?php else: ?>
|
||||
<p style="color:#8888aa;font-size:13px;margin-bottom:16px">Found <?= count($locations) ?> location(s). Copy the ID for your main location:</p>
|
||||
<?php foreach ($locations as $loc): ?>
|
||||
<div class="loc">
|
||||
<div class="loc-name"><?= htmlspecialchars($loc['name']) ?> <?= $loc['status'] === 'ACTIVE' ? '✅' : '⚠️ ' . $loc['status'] ?></div>
|
||||
<div class="loc-id-wrap">
|
||||
<div class="loc-id" id="id-<?= $loc['id'] ?>"><?= htmlspecialchars($loc['id']) ?></div>
|
||||
<button class="copy-btn" onclick="copyId('<?= $loc['id'] ?>', this)">COPY</button>
|
||||
</div>
|
||||
<div class="loc-meta">
|
||||
<?= htmlspecialchars($loc['address']['address_line_1'] ?? '') ?>
|
||||
<?= htmlspecialchars($loc['address']['city'] ?? '') ?> ·
|
||||
Currency: <?= htmlspecialchars($loc['currency'] ?? 'USD') ?> ·
|
||||
Country: <?= htmlspecialchars($loc['country'] ?? '—') ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<div class="next">
|
||||
<h2>📋 Next Steps</h2>
|
||||
<ol>
|
||||
<li>Copy your Location ID above</li>
|
||||
<li>Open <code>includes/config.php</code></li>
|
||||
<li>Replace <code>YOUR_LOCATION_ID</code> with your copied ID</li>
|
||||
<li>Also update <code>DB_PASS</code> with your MySQL password</li>
|
||||
<li><strong>Delete this file from your server!</strong></li>
|
||||
</ol>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="warn">⚠️ <strong>Security:</strong> Delete <code>get_location.php</code> from your server after use. It exposes your access token.</div>
|
||||
|
||||
<script>
|
||||
function copyId(id, btn) {
|
||||
navigator.clipboard.writeText(id).then(() => {
|
||||
btn.textContent = 'COPIED!';
|
||||
btn.style.background = '#00e676';
|
||||
setTimeout(() => { btn.textContent = 'COPY'; btn.style.background = ''; }, 2000);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+274
-229
File diff suppressed because it is too large
Load Diff
@@ -1,25 +0,0 @@
|
||||
<?php
|
||||
// Admin only diagnostic - delete after use
|
||||
$files = [
|
||||
'../../includes/db.php',
|
||||
'../../includes/auth.php',
|
||||
'../api/admin.php',
|
||||
];
|
||||
foreach ($files as $f) {
|
||||
$full = __DIR__ . '/' . $f;
|
||||
$out = shell_exec("php -l " . escapeshellarg($full) . " 2>&1");
|
||||
echo $f . ": " . trim($out) . "\n";
|
||||
}
|
||||
|
||||
// Also test DB connection directly
|
||||
try {
|
||||
require_once __DIR__ . '/../../includes/config.php';
|
||||
require_once __DIR__ . '/../../includes/db.php';
|
||||
$v = db()->query("SELECT COUNT(*) FROM users")->fetchColumn();
|
||||
echo "\nDB OK — users: $v\n";
|
||||
$v2 = db()->query("SELECT version FROM app_version ORDER BY id DESC LIMIT 1")->fetchColumn();
|
||||
echo "App version: $v2\n";
|
||||
} catch (Throwable $e) {
|
||||
echo "\nDB ERROR: " . $e->getMessage() . "\n";
|
||||
echo "File: " . $e->getFile() . " line " . $e->getLine() . "\n";
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
<?php
|
||||
echo json_encode([
|
||||
'php' => PHP_VERSION,
|
||||
'time' => date('Y-m-d H:i:s'),
|
||||
'status' => 'ok'
|
||||
]);
|
||||
@@ -1,18 +0,0 @@
|
||||
<?php
|
||||
// POST test - simulates exactly what the app does
|
||||
ob_start();
|
||||
try { require_once __DIR__ . '/../includes/auth.php'; } catch(Throwable $e) { ob_end_clean(); die(json_encode(['boot_error'=>$e->getMessage()])); }
|
||||
ob_end_clean();
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$result = loginUser($data['username'] ?? '', $data['password'] ?? '');
|
||||
if ($result['success']) unset($result['user']['password']);
|
||||
echo json_encode($result);
|
||||
exit;
|
||||
}
|
||||
|
||||
// GET - show users
|
||||
$users = db()->query("SELECT id,username,alias,is_admin,email_verified,status FROM users")->fetchAll();
|
||||
echo json_encode(['users'=>$users,'session_id'=>session_id()]);
|
||||
@@ -1,91 +0,0 @@
|
||||
<?php
|
||||
// Standalone email test - no auth required for diagnosis
|
||||
// DELETE THIS FILE after confirming email works
|
||||
|
||||
$result = [];
|
||||
$result['php_version'] = PHP_VERSION;
|
||||
$result['mail_function_exists'] = function_exists('mail');
|
||||
$result['server_software'] = $_SERVER['SERVER_SOFTWARE'] ?? 'unknown';
|
||||
$result['hostname'] = gethostname();
|
||||
|
||||
// Check sendmail path
|
||||
$sendmail = ini_get('sendmail_path');
|
||||
$result['sendmail_path'] = $sendmail ?: 'not set';
|
||||
|
||||
// Check if we can detect SMTP settings
|
||||
$result['smtp_host'] = ini_get('SMTP') ?: 'not set';
|
||||
$result['smtp_port'] = ini_get('smtp_port') ?: 'not set';
|
||||
|
||||
$to = $_POST['to'] ?? '';
|
||||
$sent = false;
|
||||
$sendError = '';
|
||||
|
||||
if ($to && filter_var($to, FILTER_VALIDATE_EMAIL) && isset($_POST['send'])) {
|
||||
$subject = 'TomTomGames Email Test';
|
||||
$message = "This is a test email from TomTomGames.\n\nIf you received this, PHP mail() is working correctly on this server.";
|
||||
$headers = "From: noreply@tomtomgames.com\r\n";
|
||||
$headers .= "Reply-To: support@tomtomgames.com\r\n";
|
||||
$headers .= "X-Mailer: PHP/" . PHP_VERSION;
|
||||
|
||||
// Capture any mail errors
|
||||
set_error_handler(function($errno, $errstr) use (&$sendError) {
|
||||
$sendError = $errstr;
|
||||
});
|
||||
$sent = mail($to, $subject, $message, $headers, '-fnoreply@tomtomgames.com');
|
||||
restore_error_handler();
|
||||
$result['mail_return'] = $sent;
|
||||
$result['mail_error'] = $sendError ?: 'none';
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Email Test</title>
|
||||
<style>
|
||||
body{font-family:monospace;background:#0a0a12;color:#e8e8f0;padding:24px;max-width:600px;margin:0 auto}
|
||||
h2{color:#f0c040}
|
||||
.box{background:#1a1a2e;border:1px solid #333;border-radius:8px;padding:16px;margin:12px 0}
|
||||
.ok{color:#00e676}.err{color:#ff4444}.warn{color:#f0c040}
|
||||
input{background:#111;border:1px solid #444;color:#fff;padding:8px 12px;width:280px;border-radius:6px;font-size:14px}
|
||||
button{background:#f0c040;color:#000;border:none;padding:10px 20px;border-radius:6px;font-weight:700;cursor:pointer;margin-left:8px}
|
||||
label{display:block;margin-bottom:6px;color:#aaa;font-size:13px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2>TomTomGames — Email Diagnostics</h2>
|
||||
|
||||
<div class="box">
|
||||
<b>Server Info:</b><br>
|
||||
PHP: <span class="ok"><?= $result['php_version'] ?></span><br>
|
||||
Server: <?= htmlspecialchars($result['server_software']) ?><br>
|
||||
Hostname: <?= htmlspecialchars($result['hostname']) ?><br>
|
||||
mail() function: <span class="<?= $result['mail_function_exists'] ? 'ok' : 'err' ?>"><?= $result['mail_function_exists'] ? 'EXISTS' : 'MISSING' ?></span><br>
|
||||
sendmail_path: <span class="warn"><?= htmlspecialchars($result['sendmail_path']) ?></span><br>
|
||||
SMTP: <span class="warn"><?= htmlspecialchars($result['smtp_host']) ?>:<?= htmlspecialchars($result['smtp_port']) ?></span>
|
||||
</div>
|
||||
|
||||
<?php if ($to): ?>
|
||||
<div class="box">
|
||||
<b>Send Result:</b><br>
|
||||
To: <?= htmlspecialchars($to) ?><br>
|
||||
mail() returned: <span class="<?= $sent ? 'ok' : 'err' ?>"><?= $sent ? 'TRUE (queued for delivery)' : 'FALSE (failed)' ?></span><br>
|
||||
Error: <span class="warn"><?= htmlspecialchars($result['mail_error']) ?></span><br>
|
||||
<?php if ($sent): ?>
|
||||
<br><span class="ok">Check your inbox (and spam folder). If nothing arrives in 5 minutes, the server is not sending outbound mail.</span>
|
||||
<?php else: ?>
|
||||
<br><span class="err">mail() returned false — server cannot send email. You need to configure SMTP (PHPMailer) or enable sendmail.</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="box">
|
||||
<form method="POST">
|
||||
<label>Send test email to:</label>
|
||||
<input type="email" name="to" value="<?= htmlspecialchars($to) ?>" placeholder="your@email.com" required>
|
||||
<button type="submit" name="send" value="1">Send Test</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<p style="color:#555;font-size:11px">Delete test_mail.php after diagnosis is complete.</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,7 +1,6 @@
|
||||
@echo off
|
||||
@echo off
|
||||
cd /d "C:\Users\myron\Downloads\tomgames"
|
||||
set /p MSG="Commit message (e.g. v1.0.2 - what changed): "
|
||||
cd /d "C:\Users\myron\Downloads\CyberPanel"
|
||||
set /p MSG="Commit message (e.g. v1.0.4 - what changed): "
|
||||
if "%MSG%"=="" (
|
||||
echo No message entered. Aborting.
|
||||
pause
|
||||
|
||||
Reference in New Issue
Block a user