diff --git a/api/uptime.php b/api/uptime.php
new file mode 100644
index 0000000..5880446
--- /dev/null
+++ b/api/uptime.php
@@ -0,0 +1,262 @@
+ ($httpCode >= 200 && $httpCode < 400) ? 'ok' : 'error',
+ 'latency' => round($latency, 2),
+ 'http_code' => $httpCode,
+ 'error' => $error ?: ($httpCode ? "HTTP $httpCode" : "Connection failed")
+ ];
+}
+
+function notifyStatusChange($url, $oldStatus, $newStatus, $error = null) {
+ $recipient = 'yumeecute@aol.com';
+ $time = date('Y-m-d H:i:s');
+
+ if ($oldStatus === 'ok' && $newStatus === 'error') {
+ $subject = "๐ด ALERT: Website Down - $url";
+ $html = "
+
+
Website Down Alert
+
The following website is currently unresponsive:
+
URL: $url
+
Error: $error
+
Time: $time
+
+
This is an automated notification from your Uptime Monitor.
+
+ ";
+ } elseif ($oldStatus === 'error' && $newStatus === 'ok') {
+ $subject = "๐ข RESOLVED: Website Up - $url";
+ $html = "
+
+
Website Back Online
+
The following website is responding normally again:
+
URL: $url
+
Time: $time
+
+
This is an automated notification from your Uptime Monitor.
+
+ ";
+ } else {
+ return; // No notification for other transitions
+ }
+
+ MailService::sendMail($recipient, $subject, $html);
+}
+
+function isMonitoringEnabled() {
+ try {
+ $stmt = db()->prepare("SELECT value FROM settings WHERE `key` = 'monitoring_enabled'");
+ $stmt->execute();
+ $val = $stmt->fetchColumn();
+ return $val === '1';
+ } catch (Exception $e) {
+ return false;
+ }
+}
+
+function generateApiKey() {
+ return bin2hex(random_bytes(32));
+}
+
+switch ($action) {
+ case 'settings':
+ echo json_encode(['monitoring_enabled' => isMonitoringEnabled()]);
+ break;
+
+ case 'toggle':
+ $data = json_decode(file_get_contents('php://input'), true);
+ $enabled = ($data['enabled'] ?? false) ? '1' : '0';
+ $stmt = db()->prepare("UPDATE settings SET value = ? WHERE `key` = 'monitoring_enabled'");
+ $stmt->execute([$enabled]);
+ echo json_encode(['success' => true, 'monitoring_enabled' => $enabled === '1']);
+ break;
+
+ case 'list':
+ $stmt = db()->query("SELECT * FROM urls ORDER BY id DESC");
+ echo json_encode($stmt->fetchAll());
+ break;
+
+ case 'add':
+ $data = json_decode(file_get_contents('php://input'), true);
+ $url = $data['url'] ?? '';
+ if (filter_var($url, FILTER_VALIDATE_URL)) {
+ // Get default team_id or first available
+ $team_id = db()->query("SELECT id FROM teams LIMIT 1")->fetchColumn();
+ if (!$team_id) {
+ // Should not happen with migrations, but safety first
+ db()->query("INSERT INTO teams (name, owner_email) VALUES ('Default Team', 'admin@example.com')");
+ $team_id = db()->lastInsertId();
+ }
+ $stmt = db()->prepare("INSERT INTO urls (url, team_id) VALUES (?, ?)");
+ $stmt->execute([$url, $team_id]);
+ echo json_encode(['success' => true, 'id' => db()->lastInsertId()]);
+ } else {
+ echo json_encode(['success' => false, 'error' => 'Invalid URL. Use http:// or https://']);
+ }
+ break;
+
+ case 'delete':
+ $data = json_decode(file_get_contents('php://input'), true);
+ $id = $data['id'] ?? 0;
+ $stmt = db()->prepare("DELETE FROM urls WHERE id = ?");
+ $stmt->execute([$id]);
+ echo json_encode(['success' => true]);
+ break;
+
+ case 'check':
+ if (!isMonitoringEnabled()) {
+ echo json_encode(['info' => 'Monitoring is disabled globally']);
+ exit;
+ }
+
+ $stmt = db()->query("SELECT * FROM urls WHERE is_active = 1");
+ $urls = $stmt->fetchAll();
+ $results = [];
+
+ foreach ($urls as $row) {
+ $res = ping($row['url']);
+
+ // Notify on change
+ notifyStatusChange($row['url'], $row['last_status'], $res['status'], $res['error']);
+
+ // Update URL status
+ $upd = db()->prepare("UPDATE urls SET last_status = ?, last_latency = ?, last_checked_at = NOW() WHERE id = ?");
+ $upd->execute([$res['status'], $res['latency'], $row['id']]);
+
+ // Insert log
+ $log = db()->prepare("INSERT INTO logs (url_id, status, latency, error_message) VALUES (?, ?, ?, ?)");
+ $log->execute([$row['id'], $res['status'], $res['latency'], $res['status'] === 'error' ? $res['error'] : null]);
+
+ $results[] = [
+ 'id' => $row['id'],
+ 'url' => $row['url'],
+ 'status' => $res['status'],
+ 'latency' => $res['latency'],
+ 'error' => $res['error']
+ ];
+ }
+ echo json_encode($results);
+ break;
+
+ case 'logs':
+ $url_id = $_GET['url_id'] ?? null;
+ if ($url_id) {
+ $stmt = db()->prepare("SELECT * FROM logs WHERE url_id = ? ORDER BY id DESC LIMIT 24");
+ $stmt->execute([$url_id]);
+ } else {
+ $stmt = db()->query("SELECT l.*, u.url FROM logs l JOIN urls u ON l.url_id = u.id ORDER BY l.id DESC LIMIT 50");
+ }
+ echo json_encode($stmt->fetchAll());
+ break;
+
+ case 'stats':
+ $res = [
+ 'total' => db()->query("SELECT COUNT(*) FROM urls")->fetchColumn(),
+ 'up' => db()->query("SELECT COUNT(*) FROM urls WHERE last_status = 'ok'")->fetchColumn(),
+ 'down' => db()->query("SELECT COUNT(*) FROM urls WHERE last_status = 'error'")->fetchColumn(),
+ 'avg_latency' => db()->query("SELECT AVG(last_latency) FROM urls")->fetchColumn() ?: 0,
+ 'recent_errors' => db()->query("SELECT l.*, u.url FROM logs l JOIN urls u ON l.url_id = u.id WHERE l.status = 'error' ORDER BY l.id DESC LIMIT 10")->fetchAll()
+ ];
+ echo json_encode($res);
+ break;
+
+ case 'teams':
+ $stmt = db()->query("SELECT * FROM teams ORDER BY id DESC");
+ echo json_encode($stmt->fetchAll());
+ break;
+
+ case 'create_team':
+ $data = json_decode(file_get_contents('php://input'), true);
+ $name = $data['name'] ?? '';
+ $email = $data['email'] ?? 'yumeecute@aol.com';
+ if ($name) {
+ $stmt = db()->prepare("INSERT INTO teams (name, owner_email) VALUES (?, ?)");
+ $stmt->execute([$name, $email]);
+ echo json_encode(['success' => true, 'id' => db()->lastInsertId()]);
+ } else {
+ echo json_encode(['success' => false, 'error' => 'Team name required']);
+ }
+ break;
+
+ case 'members':
+ $team_id = $_GET['team_id'] ?? null;
+ if (!$team_id) {
+ $team_id = db()->query("SELECT id FROM teams LIMIT 1")->fetchColumn();
+ }
+ if ($team_id) {
+ $stmt = db()->prepare("SELECT * FROM team_members WHERE team_id = ?");
+ $stmt->execute([$team_id]);
+ echo json_encode($stmt->fetchAll());
+ } else {
+ echo json_encode([]);
+ }
+ break;
+
+ case 'invite':
+ $data = json_decode(file_get_contents('php://input'), true);
+ $email = $data['email'] ?? '';
+ $team_id = $data['team_id'] ?? db()->query("SELECT id FROM teams LIMIT 1")->fetchColumn();
+ if (filter_var($email, FILTER_VALIDATE_EMAIL) && $team_id) {
+ $stmt = db()->prepare("INSERT INTO team_members (team_id, email, status) VALUES (?, ?, 'invited')");
+ $stmt->execute([$team_id, $email]);
+
+ // Send invite email
+ $subject = "Invitation to join Team on ๐๐จ๐ง๐ข๐ญ๐จ๐ซ ๐๐ฉ๐ญ๐ข๐ฆ๐ ๐๐ฒ ๐๐ฎ๐ฆ๐๐";
+ $html = "You have been invited to join a team.
Accept Invitation
";
+ MailService::sendMail($email, $subject, $html);
+
+ echo json_encode(['success' => true]);
+ } else {
+ echo json_encode(['success' => false, 'error' => 'Invalid email or team ID']);
+ }
+ break;
+
+ case 'keys':
+ $team_id = $_GET['team_id'] ?? db()->query("SELECT id FROM teams LIMIT 1")->fetchColumn();
+ if ($team_id) {
+ $stmt = db()->prepare("SELECT * FROM api_keys WHERE team_id = ?");
+ $stmt->execute([$team_id]);
+ echo json_encode($stmt->fetchAll());
+ } else {
+ echo json_encode([]);
+ }
+ break;
+
+ case 'generate_key':
+ $data = json_decode(file_get_contents('php://input'), true);
+ $label = $data['label'] ?? 'New Key';
+ $team_id = $data['team_id'] ?? db()->query("SELECT id FROM teams LIMIT 1")->fetchColumn();
+ if ($team_id) {
+ $key = generateApiKey();
+ $stmt = db()->prepare("INSERT INTO api_keys (team_id, api_key, label) VALUES (?, ?, ?)");
+ $stmt->execute([$team_id, $key, $label]);
+ echo json_encode(['success' => true, 'key' => $key]);
+ } else {
+ echo json_encode(['success' => false, 'error' => 'No team available']);
+ }
+ break;
+
+ default:
+ echo json_encode(['error' => 'Unknown action']);
+ break;
+}
\ No newline at end of file
diff --git a/db/migrations/001_create_tables.sql b/db/migrations/001_create_tables.sql
new file mode 100644
index 0000000..fcc2eac
--- /dev/null
+++ b/db/migrations/001_create_tables.sql
@@ -0,0 +1,19 @@
+CREATE TABLE IF NOT EXISTS urls (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ url VARCHAR(1024) NOT NULL,
+ is_active TINYINT(1) DEFAULT 1,
+ last_status VARCHAR(20) DEFAULT 'unknown', -- 'ok', 'error'
+ last_latency FLOAT DEFAULT 0,
+ last_checked_at TIMESTAMP NULL,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+) ENGINE=InnoDB;
+
+CREATE TABLE IF NOT EXISTS logs (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ url_id INT NOT NULL,
+ status VARCHAR(20) NOT NULL,
+ latency FLOAT DEFAULT 0,
+ error_message TEXT DEFAULT NULL,
+ checked_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (url_id) REFERENCES urls(id) ON DELETE CASCADE
+) ENGINE=InnoDB;
diff --git a/db/migrations/002_add_settings.sql b/db/migrations/002_add_settings.sql
new file mode 100644
index 0000000..58100eb
--- /dev/null
+++ b/db/migrations/002_add_settings.sql
@@ -0,0 +1,6 @@
+CREATE TABLE IF NOT EXISTS settings (
+ `key` VARCHAR(50) PRIMARY KEY,
+ `value` TEXT
+) ENGINE=InnoDB;
+
+INSERT INTO settings (`key`, `value`) VALUES ('monitoring_enabled', '0') ON DUPLICATE KEY UPDATE `key`=`key`;
diff --git a/db/migrations/003_expand_features.sql b/db/migrations/003_expand_features.sql
new file mode 100644
index 0000000..90ddbdf
--- /dev/null
+++ b/db/migrations/003_expand_features.sql
@@ -0,0 +1,44 @@
+CREATE TABLE IF NOT EXISTS teams (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ name VARCHAR(255) NOT NULL,
+ owner_email VARCHAR(255) NOT NULL,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+) ENGINE=InnoDB;
+
+CREATE TABLE IF NOT EXISTS team_members (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ team_id INT NOT NULL,
+ email VARCHAR(255) NOT NULL,
+ role VARCHAR(50) DEFAULT 'member', -- 'owner', 'admin', 'member'
+ status VARCHAR(50) DEFAULT 'active', -- 'invited', 'active'
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (team_id) REFERENCES teams(id) ON DELETE CASCADE
+) ENGINE=InnoDB;
+
+CREATE TABLE IF NOT EXISTS api_keys (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ team_id INT NOT NULL,
+ api_key VARCHAR(64) NOT NULL UNIQUE,
+ label VARCHAR(255) DEFAULT 'Default Key',
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (team_id) REFERENCES teams(id) ON DELETE CASCADE
+) ENGINE=InnoDB;
+
+-- Add team_id to urls if it doesn't exist
+SET @dbname = DATABASE();
+SET @tablename = "urls";
+SET @columnname = "team_id";
+SET @preparedStatement = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
+ WHERE TABLE_SCHEMA = @dbname AND TABLE_NAME = @tablename AND COLUMN_NAME = @columnname) > 0,
+ "SELECT 1",
+ "ALTER TABLE urls ADD COLUMN team_id INT DEFAULT NULL AFTER id, ADD FOREIGN KEY (team_id) REFERENCES teams(id) ON DELETE SET NULL"
+));
+PREPARE stmt FROM @preparedStatement;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+-- Insert a default team for existing data
+INSERT INTO teams (name, owner_email) VALUES ('Yumee Default Team', 'yumeecute@aol.com');
+SET @default_team_id = LAST_INSERT_ID();
+UPDATE urls SET team_id = @default_team_id WHERE team_id IS NULL;
diff --git a/index.php b/index.php
index 7205f3d..953f4e7 100644
--- a/index.php
+++ b/index.php
@@ -3,148 +3,586 @@ declare(strict_types=1);
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
-
-$phpVersion = PHP_VERSION;
-$now = date('Y-m-d H:i:s');
?>
-
- New Style
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ ๐๐จ๐ง๐ข๐ญ๐จ๐ซ ๐๐ฉ๐ญ๐ข๐ฆ๐ ๐๐ฒ ๐๐ฎ๐ฆ๐๐
+
-
+
+
+
+
+
-
-
Analyzing your requirements and generating your websiteโฆ
-
-
Loadingโฆ
+
+
+ [NETWORK HEALTH: 100%] [NODES ACTIVE: 12]
-
= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.
-
This page will update automatically as the plan is implemented.
-
Runtime: PHP = htmlspecialchars($phpVersion) ?> โ UTC = htmlspecialchars($now) ?>
+
+
+ 00:00:00
+
+
+
+
+
+
+
+ Total Assets
+ 0
+
+
+ Avg Latency
+ 0ms
+
+
+ Health
+ 100%
+
+
+
+
+
Asset Status (Battery View)
+
+
+
+
+
Latency Graph
+
+
+
+
+
+
+
Add Asset
+
+
+
+
+
+
+
+ | URL | Status | Latency | Actions |
+
+
+
+
+
+
Team Management
Manage your monitoring teams here.
+
API Keys
Generate keys for external integrations.
-
+
+
+
+
+
+
+
00:00:00
+
100% HEALTH
+
+
+
+
-
+