diff --git a/app/Controllers/AdminController.php b/app/Controllers/AdminController.php index a9aecd0..10f347c 100644 --- a/app/Controllers/AdminController.php +++ b/app/Controllers/AdminController.php @@ -90,7 +90,7 @@ class AdminController extends Controller { $status = $_POST['status'] ?? 'published'; $is_vip = isset($_POST['is_vip']) ? 1 : 0; - $icon_path = $this->handleUpload('icon_file'); + $icon_path = $this->handleUpload('icon_file', true); $db = db_pdo(); $stmt = $db->prepare("INSERT INTO apks (title, slug, description, version, image_url, icon_path, download_url, category_id, status, is_vip, display_order) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0)"); @@ -121,7 +121,7 @@ class AdminController extends Controller { $db = db_pdo(); $apk = $db->query("SELECT * FROM apks WHERE id = " . $params['id'])->fetch(); - $icon_path = $this->handleUpload('icon_file') ?: $apk['icon_path']; + $icon_path = $this->handleUpload('icon_file', true) ?: $apk['icon_path']; $stmt = $db->prepare("UPDATE apks SET title = ?, description = ?, version = ?, image_url = ?, icon_path = ?, download_url = ?, category_id = ?, status = ?, is_vip = ? WHERE id = ?"); $stmt->execute([$title, $description, $version, $image_url, $icon_path, $download_url, $category_id, $status, $is_vip, $params['id']]); @@ -141,7 +141,7 @@ class AdminController extends Controller { echo json_encode(['success' => true]); } - private function handleUpload($field) { + private function handleUpload($field, $compress = false) { if (!isset($_FILES[$field]) || $_FILES[$field]['error'] !== UPLOAD_ERR_OK) { return null; } @@ -155,13 +155,61 @@ class AdminController extends Controller { $fileName = uniqid() . '.' . $ext; $targetPath = $uploadDir . $fileName; - if (move_uploaded_file($_FILES[$field]['tmp_name'], $targetPath)) { - return $targetPath; + if ($compress) { + if (compress_image($_FILES[$field]['tmp_name'], $targetPath, 75)) { + return $targetPath; + } + } else { + if (move_uploaded_file($_FILES[$field]['tmp_name'], $targetPath)) { + return $targetPath; + } } return null; } + // Settings Management + public function settingsForm() { + $this->checkAuth(); + $settings = [ + 'site_name' => get_setting('site_name'), + 'site_icon' => get_setting('site_icon'), + 'site_favicon' => get_setting('site_favicon'), + 'meta_description' => get_setting('meta_description'), + 'meta_keywords' => get_setting('meta_keywords'), + 'head_js' => get_setting('head_js'), + 'body_js' => get_setting('body_js'), + ]; + $this->view('admin/settings', ['settings' => $settings]); + } + + public function saveSettings() { + $this->checkAuth(); + $db = db_pdo(); + + $fields = ['site_name', 'meta_description', 'meta_keywords', 'head_js', 'body_js']; + foreach ($fields as $field) { + if (isset($_POST[$field])) { + $stmt = $db->prepare("UPDATE settings SET setting_value = ? WHERE setting_key = ?"); + $stmt->execute([$_POST[$field], $field]); + } + } + + $site_icon = $this->handleUpload('site_icon_file'); + if ($site_icon) { + $stmt = $db->prepare("UPDATE settings SET setting_value = ? WHERE setting_key = 'site_icon'"); + $stmt->execute([$site_icon]); + } + + $site_favicon = $this->handleUpload('site_favicon_file'); + if ($site_favicon) { + $stmt = $db->prepare("UPDATE settings SET setting_value = ? WHERE setting_key = 'site_favicon'"); + $stmt->execute([$site_favicon]); + } + + $this->redirect('/admin/settings'); + } + // Category Management public function categories() { $this->checkAuth(); @@ -207,7 +255,6 @@ class AdminController extends Controller { public function rejectWithdrawal($params) { $this->checkAuth(); $db = db_pdo(); - // Refund balance if rejected? The user didn't specify, but let's do it for fairness $wd = $db->query("SELECT * FROM withdrawals WHERE id = " . $params['id'])->fetch(); if ($wd && $wd['status'] === 'pending') { $stmt = $db->prepare("UPDATE users SET balance = balance + ? WHERE id = ?"); diff --git a/app/Controllers/AuthController.php b/app/Controllers/AuthController.php index c20a9b1..248e4df 100644 --- a/app/Controllers/AuthController.php +++ b/app/Controllers/AuthController.php @@ -17,7 +17,8 @@ class AuthController extends Controller { if (isset($_SESSION['user_id'])) { $this->redirect('/profile'); } - $ref = $_GET['ref'] ?? ''; + // Check GET first, then Session + $ref = $_GET['ref'] ?? ($_SESSION['global_ref'] ?? ''); $this->view('auth/register', ['ref' => $ref]); } diff --git a/app/Controllers/HomeController.php b/app/Controllers/HomeController.php index 78eb02b..e3b4db0 100644 --- a/app/Controllers/HomeController.php +++ b/app/Controllers/HomeController.php @@ -16,6 +16,11 @@ class HomeController extends Controller { $db = db_pdo(); $category = $_GET['category'] ?? null; + // Store global referral code if present + if (isset($_GET['ref'])) { + $_SESSION['global_ref'] = $_GET['ref']; + } + $sql = "SELECT * FROM apks WHERE status = 'published'"; $params = []; @@ -32,7 +37,7 @@ class HomeController extends Controller { return $this->view('home', [ 'apks' => $apks, - 'title' => 'ApkNusa - Professional APK Download Portal' + 'title' => get_setting('site_name', 'ApkNusa') . ' - Professional APK Download Portal' ]); } @@ -47,12 +52,17 @@ class HomeController extends Controller { $this->redirect('/'); } - // Store referral code if present + // Store referral code if present specifically for this APK if (isset($_GET['ref'])) { $_SESSION['ref_download_' . $apk['id']] = $_GET['ref']; } - $this->view('apk_detail', ['apk' => $apk]); + $this->view('apk_detail', [ + 'apk' => $apk, + 'title' => 'Download ' . $apk['title'] . ' ' . $apk['version'] . ' - ' . get_setting('site_name', 'ApkNusa'), + 'meta_description' => 'Download ' . $apk['title'] . ' ' . $apk['version'] . ' APK for free. ' . substr(strip_tags($apk['description']), 0, 150) . '...', + 'meta_keywords' => $apk['title'] . ', ' . $apk['title'] . ' apk, download ' . $apk['title'] + ]); } public function download($params) { @@ -67,7 +77,9 @@ class HomeController extends Controller { } // Check for referral earnings - $ref_code = $_SESSION['ref_download_' . $apk['id']] ?? null; + // Try specific APK referral first, then global referral + $ref_code = $_SESSION['ref_download_' . $apk['id']] ?? ($_SESSION['global_ref'] ?? null); + if ($ref_code) { $stmt = $db->prepare("SELECT id FROM users WHERE referral_code = ?"); $stmt->execute([$ref_code]); @@ -91,7 +103,8 @@ class HomeController extends Controller { $stmt->execute([$referrer_id, $apk['id'], $ip]); } } - // Clear session after processing + // Clear session specific to this APK, but maybe keep global_ref? + // The user might download other APKs too. unset($_SESSION['ref_download_' . $apk['id']]); } @@ -102,4 +115,4 @@ class HomeController extends Controller { // Redirect to actual file $this->redirect($apk['download_url']); } -} \ No newline at end of file +} diff --git a/app/Controllers/SitemapController.php b/app/Controllers/SitemapController.php new file mode 100644 index 0000000..4c8f913 --- /dev/null +++ b/app/Controllers/SitemapController.php @@ -0,0 +1,52 @@ +getAllApks(); + + $db = db_pdo(); + $categories = $db->query("SELECT * FROM categories")->fetchAll(); + + header("Content-Type: application/xml; charset=utf-8"); + + $baseUrl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]"; + + echo ''; + echo ''; + + // Home + echo ''; + echo '' . $baseUrl . '/'; + echo '1.0'; + echo 'daily'; + echo ''; + + // APKs + foreach ($apks as $apk) { + echo ''; + echo '' . $baseUrl . '/apk/' . htmlspecialchars($apk['slug']) . ''; + echo '' . date('Y-m-d', strtotime($apk['created_at'] ?? 'now')) . ''; + echo '0.8'; + echo 'weekly'; + echo ''; + } + + // Categories (if you have category pages, assuming /category/slug) + foreach ($categories as $category) { + echo ''; + echo '' . $baseUrl . '/category/' . htmlspecialchars($category['slug']) . ''; + echo '0.6'; + echo 'weekly'; + echo ''; + } + + echo ''; + } +} diff --git a/app/Helpers/functions.php b/app/Helpers/functions.php index 446ffe1..3c24914 100644 --- a/app/Helpers/functions.php +++ b/app/Helpers/functions.php @@ -2,15 +2,49 @@ use App\Services\LanguageService; -function lang($key) { - return LanguageService::translate($key); -} - -function asset($path) { - return '/' . ltrim($path, '/'); -} - function db_pdo() { - require_once __DIR__ . '/../../db/config.php'; return db(); } + +function view($view, $data = []) { + extract($data); + $viewPath = __DIR__ . '/../../views/' . $view . '.php'; + if (file_exists($viewPath)) { + require $viewPath; + } else { + echo "View $view not found"; + } +} + +function redirect($path) { + header("Location: $path"); + exit; +} + +function __($key, $default = null) { + return LanguageService::translate($key, $default); +} + +function get_setting($key, $default = '') { + $db = db(); + $stmt = $db->prepare("SELECT setting_value FROM settings WHERE setting_key = ?"); + $stmt->execute([$key]); + $result = $stmt->fetch(); + return $result ? $result['setting_value'] : $default; +} + +function compress_image($source, $destination, $quality) { + $info = getimagesize($source); + if ($info['mime'] == 'image/jpeg') { + $image = imagecreatefromjpeg($source); + } elseif ($info['mime'] == 'image/gif') { + $image = imagecreatefromgif($source); + } elseif ($info['mime'] == 'image/png') { + $image = imagecreatefrompng($source); + } else { + return false; + } + + imagejpeg($image, $destination, $quality); + return $destination; +} \ No newline at end of file diff --git a/app/Services/LanguageService.php b/app/Services/LanguageService.php index 67d0c07..6557d5c 100644 --- a/app/Services/LanguageService.php +++ b/app/Services/LanguageService.php @@ -3,8 +3,43 @@ namespace App\Services; class LanguageService { - public static function translate($key) { - // Mock translation - return $key; + private static $translations = []; + private static $lang = 'id'; + + public static function init() { + if (isset($_SESSION['lang'])) { + self::$lang = $_SESSION['lang']; + } elseif (isset($_COOKIE['lang'])) { + self::$lang = $_COOKIE['lang']; + } + + $langFile = __DIR__ . '/../../lang/' . self::$lang . '.php'; + if (file_exists($langFile)) { + self::$translations = require $langFile; + } else { + // Default to English if the translation file doesn't exist + self::$lang = 'en'; + $langFile = __DIR__ . '/../../lang/' . self::$lang . '.php'; + if (file_exists($langFile)) { + self::$translations = require $langFile; + } + } } -} + + public static function translate($key, $default = null) { + if (empty(self::$translations)) { + self::init(); + } + return self::$translations[$key] ?? ($default ?? $key); + } + + public static function setLang($lang) { + self::$lang = $lang; + $_SESSION['lang'] = $lang; + setcookie('lang', $lang, time() + (86400 * 30), "/"); + } + + public static function getLang() { + return self::$lang; + } +} \ No newline at end of file diff --git a/db/migrations/20260224_add_seo_and_ads_settings.sql b/db/migrations/20260224_add_seo_and_ads_settings.sql new file mode 100644 index 0000000..bc2c387 --- /dev/null +++ b/db/migrations/20260224_add_seo_and_ads_settings.sql @@ -0,0 +1,5 @@ +INSERT IGNORE INTO settings (setting_key, setting_value) VALUES +('meta_description', 'Download the latest APKs for free.'), +('meta_keywords', 'apk, download, android, games, apps'), +('head_js', ''), +('body_js', ''); diff --git a/index.php b/index.php index c7abfad..4e5451a 100644 --- a/index.php +++ b/index.php @@ -1,16 +1,46 @@ get('/sitemap.xml', 'SitemapController@index'); + +// Language Switch +$router->get('/lang/:code', function($params) { + $code = $params['code']; + \App\Services\LanguageService::setLang($code); + header('Location: ' . ($_SERVER['HTTP_REFERER'] ?? '/')); + exit; +}); + // Home & APKs $router->get('/', 'HomeController@index'); $router->get('/apk/:slug', 'HomeController@apkDetail'); @@ -33,6 +63,10 @@ $router->get('/admin/logout', 'AdminController@logout'); // Admin Dashboard $router->get('/admin/dashboard', 'AdminController@dashboard'); +// Admin Settings +$router->get('/admin/settings', 'AdminController@settingsForm'); +$router->post('/admin/settings', 'AdminController@saveSettings'); + // Admin APKs $router->get('/admin/apks', 'AdminController@apks'); $router->get('/admin/apks/add', 'AdminController@addApkForm'); diff --git a/install.php b/install.php new file mode 100644 index 0000000..a0a1e60 --- /dev/null +++ b/install.php @@ -0,0 +1,174 @@ + PDO::ERRMODE_EXCEPTION + ]); + $pdo->exec("CREATE DATABASE IF NOT EXISTS `$name` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"); + $pdo->exec("USE `$name`"); + + // Save config + $configContent = " PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + ]);\n }\n return \$pdo; +}\n"; + file_put_contents('db/config.php', $configContent); + + $_SESSION['install_pdo'] = ['host' => $host, 'name' => $name, 'user' => $user, 'pass' => $pass]; + header("Location: install.php?step=3"); + exit; + } catch (PDOException $e) { + $error = "Database Error: " . $e->getMessage(); + } + } + + if ($step == 3) { + require_once 'db/config.php'; + $db = db(); + + // Import Schema + $schemaFile = 'full_schema.sql'; + if (file_exists($schemaFile)) { + $sql = file_get_contents($schemaFile); + $db->exec($sql); + } else { + // Basic tables if full_schema.sql is missing + $db->exec("CREATE TABLE IF NOT EXISTS users (id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) UNIQUE, password VARCHAR(255), balance DECIMAL(10,2) DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)"); + $db->exec("CREATE TABLE IF NOT EXISTS settings (id INT AUTO_INCREMENT PRIMARY KEY, setting_key VARCHAR(255) UNIQUE, setting_value TEXT)"); + $db->exec("INSERT IGNORE INTO settings (setting_key, setting_value) VALUES ('site_name', 'My APK Store'), ('site_icon', ''), ('site_favicon', '')"); + } + + // Create Admin + $admin_user = $_POST['admin_user']; + $admin_pass = password_hash($_POST['admin_pass'], PASSWORD_DEFAULT); + + $stmt = $db->prepare("INSERT INTO users (username, password) VALUES (?, ?)"); + $stmt->execute([$admin_user, $admin_pass]); + + $success = "Installation complete!"; + $step = 4; + } +} + +?> + + + + + Installer + + + + + +
+

Web Installer

+ +
+
1
+
2
+
3
+
4
+
+ + +
+ + + +
Welcome
+

This wizard will help you install the application on your server.

+
+ Start Installation +
+ + + +
Database Configuration
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ + + +
Admin Account
+
+
+ + +
+
+ + +
+
+ +
+
+ + + +
+

+
Installation Successful!
+

You can now log in to your admin panel.

+
+ Important: Please delete install.php file from your server. +
+ +
+ +
+ + + diff --git a/lang/en.php b/lang/en.php new file mode 100644 index 0000000..1286742 --- /dev/null +++ b/lang/en.php @@ -0,0 +1,35 @@ + 'Home', + 'search' => 'Search...', + 'login' => 'Login', + 'register' => 'Register', + 'logout' => 'Logout', + 'profile' => 'Profile', + 'admin' => 'Admin', + 'download' => 'Download', + 'share' => 'Share', + 'categories' => 'Categories', + 'all_categories' => 'All Categories', + 'latest_apks' => 'Latest APKs', + 'featured_apks' => 'Featured APKs', + 'download_now' => 'Download Now', + 'share_link' => 'Share Link', + 'balance' => 'Balance', + 'referral_link' => 'Referral Link', + 'withdraw' => 'Withdraw', + 'withdrawal_history' => 'Withdrawal History', + 'settings' => 'Settings', + 'site_name' => 'Site Name', + 'site_icon' => 'Site Icon', + 'site_favicon' => 'Site Favicon', + 'save_settings' => 'Save Settings', + 'select_language' => 'Select Language', + 'language_indonesia' => 'Indonesian', + 'language_english' => 'English', + 'admin_dashboard' => 'Admin Dashboard', + 'manage_apks' => 'Manage APKs', + 'manage_categories' => 'Manage Categories', + 'manage_withdrawals' => 'Manage Withdrawals', + 'general_settings' => 'General Settings', +]; diff --git a/lang/id.php b/lang/id.php new file mode 100644 index 0000000..7989019 --- /dev/null +++ b/lang/id.php @@ -0,0 +1,35 @@ + 'Beranda', + 'search' => 'Cari...', + 'login' => 'Masuk', + 'register' => 'Daftar', + 'logout' => 'Keluar', + 'profile' => 'Profil', + 'admin' => 'Admin', + 'download' => 'Unduh', + 'share' => 'Bagikan', + 'categories' => 'Kategori', + 'all_categories' => 'Semua Kategori', + 'latest_apks' => 'APK Terbaru', + 'featured_apks' => 'APK Unggulan', + 'download_now' => 'Unduh Sekarang', + 'share_link' => 'Bagikan Link', + 'balance' => 'Saldo', + 'referral_link' => 'Link Referral', + 'withdraw' => 'Tarik Saldo', + 'withdrawal_history' => 'Riwayat Penarikan', + 'settings' => 'Pengaturan', + 'site_name' => 'Nama Website', + 'site_icon' => 'Ikon Website', + 'site_favicon' => 'Favicon Website', + 'save_settings' => 'Simpan Pengaturan', + 'select_language' => 'Pilih Bahasa', + 'language_indonesia' => 'Indonesia', + 'language_english' => 'English', + 'admin_dashboard' => 'Dashboard Admin', + 'manage_apks' => 'Kelola APK', + 'manage_categories' => 'Kelola Kategori', + 'manage_withdrawals' => 'Kelola Penarikan', + 'general_settings' => 'Pengaturan Umum', +]; diff --git a/views/admin/apks/form.php b/views/admin/apks/form.php index d2adf5a..194ca9b 100644 --- a/views/admin/apks/form.php +++ b/views/admin/apks/form.php @@ -5,26 +5,26 @@
-
+
- +
- +
- +
@@ -32,26 +32,29 @@
- +
- - + + - +
+ +
Icons are automatically compressed to optimize speed.
+
- +
- +
@@ -65,14 +68,14 @@
> - +
-
- Cancel - +
+ Cancel +
@@ -81,4 +84,4 @@
- \ No newline at end of file + diff --git a/views/admin/dashboard.php b/views/admin/dashboard.php index 2d55ead..df6d2a1 100644 --- a/views/admin/dashboard.php +++ b/views/admin/dashboard.php @@ -2,25 +2,28 @@
-

Dashboard Overview

+

-
+
-
Total APKs
-
+
Total APKs
+
- +
@@ -28,15 +31,15 @@
-
+
-
Total Downloads
-
+
Total Downloads
+
- +
@@ -44,15 +47,15 @@
-
+
-
Total Users
-
+
Total Users
+
- +
@@ -60,15 +63,15 @@
-
+
-
Pending Withdrawals
-
+
Pending Withdrawals
+
- +
@@ -78,32 +81,32 @@
-
-
-
Recent APKs
+
+
+
Recent APKs
- - - - + + + + - - + + @@ -117,24 +120,27 @@
-
-
-
Quick Navigation
+
- \ No newline at end of file + diff --git a/views/admin/header.php b/views/admin/header.php index b7f44ce..68ee69b 100644 --- a/views/admin/header.php +++ b/views/admin/header.php @@ -1,11 +1,12 @@ - + - Admin Panel - APK Portal + <?php echo __('admin_dashboard'); ?> - <?php echo htmlspecialchars(get_setting('site_name', 'APK ADMIN')); ?> - + +
- + \ No newline at end of file diff --git a/views/admin/settings.php b/views/admin/settings.php new file mode 100644 index 0000000..0111e09 --- /dev/null +++ b/views/admin/settings.php @@ -0,0 +1,96 @@ + + +
+
+
+
+
+
+
+
+
+
+ + +
+ +
+
+ + + +
+ Icon +
+ +
+
+ + + +
+ Favicon +
+ +
+
+ +
+
SEO Settings
+ +
+ + +
Brief description of your site for search engines.
+
+ +
+ + +
Comma separated keywords (e.g. apk, games, mod).
+
+ +
+
Custom Scripts & Ads
+ +
+ + +
Injected before </head>.
+
+ +
+ + +
Injected before </body>.
+
+ +
+ +
+ +
+
+ + +
+
+
+ + \ No newline at end of file diff --git a/views/auth/profile.php b/views/auth/profile.php index de2ebf9..8f1194f 100644 --- a/views/auth/profile.php +++ b/views/auth/profile.php @@ -6,14 +6,14 @@
- +

Member since


-

+

Points
@@ -26,10 +26,10 @@
-
Current Balance
+

Rp

Min. withdraw: Rp 10.000

@@ -46,12 +46,12 @@
-
My Referral Code
+

Share your referral link to earn Rp 500 for every download.

- +
Example APK referral link:
@@ -63,7 +63,7 @@
-
Withdrawal History
+
Recent activities
diff --git a/views/footer.php b/views/footer.php index a21e1d9..9303d2f 100644 --- a/views/footer.php +++ b/views/footer.php @@ -1,13 +1,18 @@ - +
- - ApkNusa + + + Logo + + + +

- ApkNusa is your premier source for professional APK downloads, offering the latest and safest Android applications and games. + is your premier source for professional APK downloads, offering the latest and safest Android applications and games.

@@ -38,7 +43,7 @@
- © ApkNusa. All rights reserved. + © . All rights reserved.
@@ -53,5 +58,6 @@
+ - + \ No newline at end of file diff --git a/views/header.php b/views/header.php index a2df323..5514538 100644 --- a/views/header.php +++ b/views/header.php @@ -1,22 +1,34 @@ - + - <?php echo $title ?? 'ApkNusa'; ?> - + <?php echo $title ?? htmlspecialchars(get_setting('site_name', 'ApkNusa')); ?> + + + + - + + + + +
-
+ + +
-
+
-
-
+
+
- <?php echo $apk['title']; ?> -
-
- v - - VIP - + <?php echo $apk['title']; ?> +
+
+ v
-

-
- - Details +

+
+ + Details
@@ -79,4 +78,16 @@
+ + \ No newline at end of file
TitleVersionDownloadsStatusTitleVersionDownloadsStatus
-
- +
+
vv - +