From 40c575f025f82d6d26b19c6e2a31b48180847ccd Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 24 Oct 2025 15:11:56 +0000
Subject: [PATCH] Add My Account page with menu integration and config file
Co-authored-by: iaretechnician <2749183+iaretechnician@users.noreply.github.com>
---
.github/copilot-instructions.md | 3 +
modules/billing/css/header.css | 2 +
modules/billing/includes/config.inc.php | 35 ++
modules/billing/includes/menu.php | 3 +
modules/billing/my_account.php | 491 ++++++++++++++++++++++++
5 files changed, 534 insertions(+)
create mode 100644 modules/billing/includes/config.inc.php
create mode 100644 modules/billing/my_account.php
diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
index b97ca1fd..3b8f0c90 100644
--- a/.github/copilot-instructions.md
+++ b/.github/copilot-instructions.md
@@ -12,6 +12,7 @@
- `_website/` — canonical website storefront and Checkout/Webhooks flow.
- `modules/config_games/server_configs/` — authoritative game catalog XMLs (all supported games live here).
- `modules/` — panel modules (legacy `billing/` exists; its **schema** is authoritative for multi-remote, but the **pages** are deprecated).
+- `modules/billing/` — frontend website for selling gameservers to customers. Can interface with panel from same machine or external web host via MySQL tables. Uses `gameservers_website` session namespace (separate from panel sessions).
- `includes/` — panel configuration and DB connectors.
- `ogp_api.php` — internal API entry point for panel-side actions.
- `api/` — Payment-related API code if present in this branch (previously under `paypal/` or `payments/`).
@@ -26,6 +27,8 @@
## 3) Scope & principles
- **Website ↔ Panel on the same host.** Website uses the **panel DB for authentication** and the **panel’s internal APIs** for provisioning. **Sessions remain separate** (website session ≠ panel session).
+- **Billing module flexibility.** The `modules/billing/` frontend can run on the **same machine as the panel** or on an **external web host**, interfacing primarily via MySQL table edits. All interaction with panel DB happens through direct MySQL queries using credentials in `modules/billing/includes/config.inc.php`.
+- **Billing module flexibility.** The `modules/billing/` frontend can run on the **same machine as the panel** or on an **external web host**, interfacing primarily via MySQL table edits. All interaction with panel DB happens through direct MySQL queries using credentials in `modules/billing/includes/config.inc.php`.
- **Catalog = XML.** Enable **every game** present under `modules/config_games/server_configs/`. The website reads those XMLs for ports, params, install/update metadata. New XMLs should become available without code changes.
- **Regions/Nodes = panel DB.** Regions and nodes are configured in the panel and must be **queried live** from the panel DB. Never hardcode or mirror region lists on the website.
- **Slotless model.** Pricing/UX must not enforce slot caps. If an engine requires a player count parameter, set a safe high default and surface engine limits transparently if they exist.
diff --git a/modules/billing/css/header.css b/modules/billing/css/header.css
index 757eb0d7..af64f4df 100644
--- a/modules/billing/css/header.css
+++ b/modules/billing/css/header.css
@@ -24,6 +24,8 @@
.gsw-header-nav{display:flex;gap:22px;align-items:center;}
.gsw-nav-link{color:#fff;text-decoration:none;font-size:0.98rem;transition:opacity 0.2s;padding:6px 8px;border-radius:6px;}
.gsw-nav-link:hover{opacity:0.9;text-decoration:underline;background:rgba(255,255,255,0.03);}
+/* My Account link styling - larger font in middle of menu */
+.gsw-nav-link-myaccount{font-size:1.15rem;font-weight:600;padding:6px 12px;}
.gsw-user-info{color:#fff;font-size:0.95rem;margin-right:8px;}
diff --git a/modules/billing/includes/config.inc.php b/modules/billing/includes/config.inc.php
new file mode 100644
index 00000000..4401518c
--- /dev/null
+++ b/modules/billing/includes/config.inc.php
@@ -0,0 +1,35 @@
+
diff --git a/modules/billing/includes/menu.php b/modules/billing/includes/menu.php
index 819aa6b2..0ad4b635 100644
--- a/modules/billing/includes/menu.php
+++ b/modules/billing/includes/menu.php
@@ -90,6 +90,7 @@ if ($is_logged_in) {
Home
Game Servers
+ My Account
My Servers
Cart
0) echo ' ' . intval($cart_count) . '';
?>
+
+ Login
Register
diff --git a/modules/billing/my_account.php b/modules/billing/my_account.php
new file mode 100644
index 00000000..0e2656e5
--- /dev/null
+++ b/modules/billing/my_account.php
@@ -0,0 +1,491 @@
+
+
+
+
+
+ My Account - GameServers.World
+
+
+
+ 0) {
+ $query = "SELECT user_id, users_login, users_email, users_fname, users_lname FROM ogp_users WHERE user_id = $user_id LIMIT 1";
+ $result = mysqli_query($db, $query);
+ if ($result && mysqli_num_rows($result) === 1) {
+ $user_info = mysqli_fetch_assoc($result);
+ }
+}
+
+// Handle password change
+if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['change_password'])) {
+ $current_password = $_POST['current_password'] ?? '';
+ $new_password = $_POST['new_password'] ?? '';
+ $confirm_password = $_POST['confirm_password'] ?? '';
+
+ if (empty($current_password) || empty($new_password) || empty($confirm_password)) {
+ $error_message = 'All password fields are required.';
+ } elseif ($new_password !== $confirm_password) {
+ $error_message = 'New passwords do not match.';
+ } elseif (strlen($new_password) < 6) {
+ $error_message = 'New password must be at least 6 characters long.';
+ } else {
+ // Verify current password (using MD5 as per panel legacy)
+ $current_hash = md5($current_password);
+ $verify_query = "SELECT user_id FROM ogp_users WHERE user_id = $user_id AND users_passwd = '$current_hash' LIMIT 1";
+ $verify_result = mysqli_query($db, $verify_query);
+
+ if ($verify_result && mysqli_num_rows($verify_result) === 1) {
+ // Update password
+ $new_hash = md5($new_password);
+ $update_query = "UPDATE ogp_users SET users_passwd = '$new_hash' WHERE user_id = $user_id LIMIT 1";
+ if (mysqli_query($db, $update_query)) {
+ $success_message = 'Password changed successfully!';
+ } else {
+ $error_message = 'Failed to update password. Please try again.';
+ }
+ } else {
+ $error_message = 'Current password is incorrect.';
+ }
+ }
+}
+
+// Handle account info update
+if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_info'])) {
+ $fname = mysqli_real_escape_string($db, trim($_POST['fname'] ?? ''));
+ $lname = mysqli_real_escape_string($db, trim($_POST['lname'] ?? ''));
+ $email = mysqli_real_escape_string($db, trim($_POST['email'] ?? ''));
+
+ if (!empty($email) && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
+ $error_message = 'Invalid email address.';
+ } else {
+ $update_query = "UPDATE ogp_users SET users_fname = '$fname', users_lname = '$lname', users_email = '$email' WHERE user_id = $user_id LIMIT 1";
+ if (mysqli_query($db, $update_query)) {
+ $success_message = 'Account information updated successfully!';
+ // Refresh user info
+ $query = "SELECT user_id, users_login, users_email, users_fname, users_lname FROM ogp_users WHERE user_id = $user_id LIMIT 1";
+ $result = mysqli_query($db, $query);
+ if ($result && mysqli_num_rows($result) === 1) {
+ $user_info = mysqli_fetch_assoc($result);
+ }
+ } else {
+ $error_message = 'Failed to update account information. Please try again.';
+ }
+ }
+}
+
+// Fetch user's game servers from billing_orders
+$servers_query = "SELECT
+ o.order_id,
+ o.home_name,
+ o.status,
+ o.price,
+ o.invoice_duration,
+ o.created_at,
+ bs.service_name,
+ rs.remote_server_name
+ FROM ogp_billing_orders o
+ LEFT JOIN ogp_billing_services bs ON o.service_id = bs.service_id
+ LEFT JOIN ogp_remote_servers rs ON o.remote_server_id = rs.remote_server_id
+ WHERE o.user_id = $user_id
+ ORDER BY o.created_at DESC";
+$servers_result = mysqli_query($db, $servers_query);
+
+// Fetch invoices (from data directory JSON files)
+$dataDir = (isset($SITE_DATA_DIR) && $SITE_DATA_DIR) ? $SITE_DATA_DIR : realpath(__DIR__ . '/') . DIRECTORY_SEPARATOR . 'data';
+$invoices = [];
+if (is_dir($dataDir)) {
+ foreach (glob($dataDir . '/*.json') as $file) {
+ $j = json_decode(file_get_contents($file), true);
+ if (!$j || !is_array($j)) continue;
+
+ // Try to match by user email or user_id in custom field
+ $match = false;
+ if ($user_info && !empty($user_info['users_email'])) {
+ if (!empty($j['payer']) && stripos($j['payer'], $user_info['users_email']) !== false) $match = true;
+ if (!$match && !empty($j['custom']) && stripos($j['custom'], $user_info['users_email']) !== false) $match = true;
+ }
+
+ if ($match) {
+ $invoices[] = $j;
+ }
+ }
+}
+
+// Sort invoices by date (newest first)
+usort($invoices, function($a, $b) {
+ return strtotime($b['ts'] ?? 0) - strtotime($a['ts'] ?? 0);
+});
+
+// Separate current (pending) and previous (paid) invoices
+$current_invoices = array_filter($invoices, function($inv) {
+ return strtolower($inv['status'] ?? '') === 'pending' || empty($inv['status']);
+});
+$previous_invoices = array_filter($invoices, function($inv) {
+ return strtolower($inv['status'] ?? '') === 'paid' || strtolower($inv['status'] ?? '') === 'completed';
+});
+
+?>
+
+
+
+
+
+
+
+
+
+
+
+
+
Account Information
+
+
+
+
+
+ Edit Account Information
+
+
+
+
Unable to load account information.
+
+
+
+
+
+
+
+
+
My Game Servers
+ 0): ?>
+
+
+
+
+
+ Game:
+
+
+
+ Location:
+
+
+
+ Status:
+
+
+
+ Price:
+ $/
+
+
+ Created:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Current Invoices Due
+
+
+
+
+
+
+
+
+
Previous Invoices
+
+
+
+
+
+
No previous invoices found.
+
+
+
+
+
+
+
+
+