diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..ab30b03 --- /dev/null +++ b/.htaccess @@ -0,0 +1,9 @@ +RewriteEngine On +RewriteBase / + +# Stop processing if file or directory exists +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d + +# Redirect all other requests to index.php +RewriteRule ^(.*)$ index.php [L,QSA] diff --git a/app/Controllers/ApkController.php b/app/Controllers/ApkController.php new file mode 100644 index 0000000..ad1aca3 --- /dev/null +++ b/app/Controllers/ApkController.php @@ -0,0 +1,38 @@ +apkService = new ApkService(); + } + + public function detail($params) { + $apk = $this->apkService->getBySlug($params['slug']); + if (!$apk) { + header("HTTP/1.0 404 Not Found"); + echo "APK Not Found"; + return; + } + + return $this->view('apk_detail', [ + 'apk' => $apk, + 'title' => $apk['title'] . ' v' . $apk['version'] . ' APK Download' + ]); + } + + public function download($params) { + $apk = $this->apkService->getBySlug($params['slug']); + if ($apk) { + $this->apkService->incrementDownload($apk['id']); + // In a real app, this would be a link to a file or a CDN. + // For now, let's redirect to a mock download URL or back. + $this->redirect($apk['download_url'] === '#' ? '/apk/' . $apk['slug'] . '?downloaded=1' : $apk['download_url']); + } + } +} diff --git a/app/Controllers/HomeController.php b/app/Controllers/HomeController.php new file mode 100644 index 0000000..aa23f18 --- /dev/null +++ b/app/Controllers/HomeController.php @@ -0,0 +1,22 @@ +apkService = new ApkService(); + } + + public function index() { + $apks = $this->apkService->getLatest(12); + return $this->view('home', [ + 'apks' => $apks, + 'title' => 'ApkNusa - Professional APK Download Portal' + ]); + } +} diff --git a/app/Core/Controller.php b/app/Core/Controller.php new file mode 100644 index 0000000..cddbafe --- /dev/null +++ b/app/Core/Controller.php @@ -0,0 +1,25 @@ +[^/]+)', $path); + $this->routes[] = [ + 'method' => strtoupper($method), + 'path' => '#^' . $path . '$#', + 'handler' => $handler + ]; + } + + public function dispatch($method, $uri) { + $uri = parse_url($uri, PHP_URL_PATH); + + foreach ($this->routes as $route) { + if ($route['method'] === strtoupper($method) && preg_match($route['path'], $uri, $matches)) { + $handler = $route['handler']; + $params = array_filter($matches, 'is_string', ARRAY_FILTER_USE_KEY); + + if (is_array($handler)) { + [$controllerClass, $methodName] = $handler; + $controller = new $controllerClass(); + return $controller->$methodName($params); + } + + return $handler($params); + } + } + + header("HTTP/1.0 404 Not Found"); + echo "404 Not Found"; + } +} diff --git a/app/Services/ApkService.php b/app/Services/ApkService.php new file mode 100644 index 0000000..1654023 --- /dev/null +++ b/app/Services/ApkService.php @@ -0,0 +1,37 @@ +db = db(); + } + + public function getLatest($limit = 10) { + $stmt = $this->db->prepare("SELECT * FROM apks WHERE status = 'published' ORDER BY created_at DESC LIMIT :limit"); + $stmt->bindValue(':limit', (int)$limit, \PDO::PARAM_INT); + $stmt->execute(); + return $stmt->fetchAll(); + } + + public function getBySlug($slug) { + $stmt = $this->db->prepare("SELECT * FROM apks WHERE slug = :slug AND status = 'published' LIMIT 1"); + $stmt->execute(['slug' => $slug]); + return $stmt->fetch(); + } + + public function incrementDownload($apkId) { + $stmt = $this->db->prepare("UPDATE apks SET total_downloads = total_downloads + 1 WHERE id = :id"); + $stmt->execute(['id' => $apkId]); + + $stmt = $this->db->prepare("INSERT INTO downloads (apk_id, ip_address) VALUES (:apk_id, :ip)"); + $stmt->execute([ + 'apk_id' => $apkId, + 'ip' => $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0' + ]); + } +} diff --git a/assets/css/custom.css b/assets/css/custom.css index 50e0502..e0783c0 100644 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -1,302 +1,72 @@ body { - background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab); - background-size: 400% 400%; - animation: gradient 15s ease infinite; - color: #212529; - font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; - font-size: 14px; - margin: 0; - min-height: 100vh; + font-family: 'Inter', sans-serif; + background-color: #F5F7F9; + color: #263238; } -.main-wrapper { - display: flex; - align-items: center; - justify-content: center; - min-height: 100vh; - width: 100%; - padding: 20px; - box-sizing: border-box; - position: relative; - z-index: 1; +.hover-lift { + transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out; } -@keyframes gradient { - 0% { - background-position: 0% 50%; - } - 50% { - background-position: 100% 50%; - } - 100% { - background-position: 0% 50%; - } +.hover-lift:hover { + transform: translateY(-5px); + box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.1) !important; } -.chat-container { - width: 100%; - max-width: 600px; - background: rgba(255, 255, 255, 0.85); - border: 1px solid rgba(255, 255, 255, 0.3); - border-radius: 20px; - display: flex; - flex-direction: column; - height: 85vh; - box-shadow: 0 20px 40px rgba(0,0,0,0.2); - backdrop-filter: blur(15px); - -webkit-backdrop-filter: blur(15px); +.line-clamp-2 { + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; overflow: hidden; } -.chat-header { - padding: 1.5rem; - border-bottom: 1px solid rgba(0, 0, 0, 0.05); - background: rgba(255, 255, 255, 0.5); - font-weight: 700; - font-size: 1.1rem; - display: flex; - justify-content: space-between; - align-items: center; +.floating-animation { + animation: floating 3s ease-in-out infinite; } -.chat-messages { - flex: 1; - overflow-y: auto; - padding: 1.5rem; - display: flex; - flex-direction: column; - gap: 1.25rem; +@keyframes floating { + 0% { transform: translate(0, 0px); } + 50% { transform: translate(0, 15px); } + 100% { transform: translate(0, -0px); } } -/* Custom Scrollbar */ -::-webkit-scrollbar { - width: 6px; +.btn-success { + background-color: #00C853; + border-color: #00C853; } -::-webkit-scrollbar-track { - background: transparent; +.btn-success:hover { + background-color: #00B248; + border-color: #00B248; } -::-webkit-scrollbar-thumb { - background: rgba(255, 255, 255, 0.3); - border-radius: 10px; +.text-success { + color: #00C853 !important; } -::-webkit-scrollbar-thumb:hover { - background: rgba(255, 255, 255, 0.5); +.bg-success { + background-color: #00C853 !important; } -.message { - max-width: 85%; - padding: 0.85rem 1.1rem; - border-radius: 16px; - line-height: 1.5; - font-size: 0.95rem; - box-shadow: 0 4px 15px rgba(0,0,0,0.05); - animation: fadeIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); +.bg-success-subtle { + background-color: #E8F5E9 !important; } -@keyframes fadeIn { - from { opacity: 0; transform: translateY(20px) scale(0.95); } - to { opacity: 1; transform: translateY(0) scale(1); } -} - -.message.visitor { - align-self: flex-end; - background: linear-gradient(135deg, #212529 0%, #343a40 100%); - color: #fff; - border-bottom-right-radius: 4px; -} - -.message.bot { - align-self: flex-start; - background: #ffffff; - color: #212529; - border-bottom-left-radius: 4px; -} - -.chat-input-area { - padding: 1.25rem; - background: rgba(255, 255, 255, 0.5); - border-top: 1px solid rgba(0, 0, 0, 0.05); -} - -.chat-input-area form { - display: flex; - gap: 0.75rem; -} - -.chat-input-area input { - flex: 1; - border: 1px solid rgba(0, 0, 0, 0.1); - border-radius: 12px; - padding: 0.75rem 1rem; - outline: none; - background: rgba(255, 255, 255, 0.9); - transition: all 0.3s ease; -} - -.chat-input-area input:focus { - border-color: #23a6d5; - box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.2); -} - -.chat-input-area button { - background: #212529; - color: #fff; - border: none; - padding: 0.75rem 1.5rem; - border-radius: 12px; - cursor: pointer; - font-weight: 600; - transition: all 0.3s ease; -} - -.chat-input-area button:hover { - background: #000; - transform: translateY(-2px); - box-shadow: 0 5px 15px rgba(0,0,0,0.2); -} - -/* Background Animations */ -.bg-animations { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 0; - overflow: hidden; - pointer-events: none; -} - -.blob { - position: absolute; - width: 500px; - height: 500px; - background: rgba(255, 255, 255, 0.2); - border-radius: 50%; - filter: blur(80px); - animation: move 20s infinite alternate cubic-bezier(0.45, 0, 0.55, 1); -} - -.blob-1 { - top: -10%; - left: -10%; - background: rgba(238, 119, 82, 0.4); -} - -.blob-2 { - bottom: -10%; - right: -10%; - background: rgba(35, 166, 213, 0.4); - animation-delay: -7s; - width: 600px; - height: 600px; -} - -.blob-3 { - top: 40%; - left: 30%; - background: rgba(231, 60, 126, 0.3); - animation-delay: -14s; - width: 450px; - height: 450px; -} - -@keyframes move { - 0% { transform: translate(0, 0) rotate(0deg) scale(1); } - 33% { transform: translate(150px, 100px) rotate(120deg) scale(1.1); } - 66% { transform: translate(-50px, 200px) rotate(240deg) scale(0.9); } - 100% { transform: translate(0, 0) rotate(360deg) scale(1); } -} - -.admin-link { - font-size: 14px; - color: #fff; - text-decoration: none; - background: rgba(0, 0, 0, 0.2); - padding: 0.5rem 1rem; - border-radius: 8px; - transition: all 0.3s ease; -} - -.admin-link:hover { - background: rgba(0, 0, 0, 0.4); - text-decoration: none; -} - -/* Admin Styles */ -.admin-container { - max-width: 900px; - margin: 3rem auto; - padding: 2.5rem; - background: rgba(255, 255, 255, 0.85); - backdrop-filter: blur(20px); - -webkit-backdrop-filter: blur(20px); - border-radius: 24px; - box-shadow: 0 20px 50px rgba(0,0,0,0.15); - border: 1px solid rgba(255, 255, 255, 0.4); - position: relative; - z-index: 1; -} - -.admin-container h1 { - margin-top: 0; - color: #212529; - font-weight: 800; -} - -.table { - width: 100%; - border-collapse: separate; - border-spacing: 0 8px; - margin-top: 1.5rem; -} - -.table th { - background: transparent; - border: none; - padding: 1rem; - color: #6c757d; - font-weight: 600; - text-transform: uppercase; - font-size: 0.75rem; - letter-spacing: 1px; -} - -.table td { - background: #fff; - padding: 1rem; +.btn-white { + background-color: #FFFFFF; + color: #00C853; border: none; } -.table tr td:first-child { border-radius: 12px 0 0 12px; } -.table tr td:last-child { border-radius: 0 12px 12px 0; } - -.form-group { - margin-bottom: 1.25rem; +.btn-white:hover { + background-color: #F5F7F9; + color: #00B248; } -.form-group label { - display: block; - margin-bottom: 0.5rem; - font-weight: 600; - font-size: 0.9rem; -} +.rounded-4 { border-radius: 1rem !important; } +.rounded-5 { border-radius: 1.5rem !important; } -.form-control { - width: 100%; - padding: 0.75rem 1rem; - border: 1px solid rgba(0, 0, 0, 0.1); - border-radius: 12px; - background: #fff; - transition: all 0.3s ease; - box-sizing: border-box; +.navbar-brand i { + font-size: 1.5rem; + vertical-align: middle; } - -.form-control:focus { - outline: none; - border-color: #23a6d5; - box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.1); -} \ No newline at end of file diff --git a/assets/js/main.js b/assets/js/main.js index d349598..236a20d 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -1,39 +1,11 @@ document.addEventListener('DOMContentLoaded', () => { - const chatForm = document.getElementById('chat-form'); - const chatInput = document.getElementById('chat-input'); - const chatMessages = document.getElementById('chat-messages'); - - const appendMessage = (text, sender) => { - const msgDiv = document.createElement('div'); - msgDiv.classList.add('message', sender); - msgDiv.textContent = text; - chatMessages.appendChild(msgDiv); - chatMessages.scrollTop = chatMessages.scrollHeight; - }; - - chatForm.addEventListener('submit', async (e) => { - e.preventDefault(); - const message = chatInput.value.trim(); - if (!message) return; - - appendMessage(message, 'visitor'); - chatInput.value = ''; - - try { - const response = await fetch('api/chat.php', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ message }) - }); - const data = await response.json(); - - // Artificial delay for realism - setTimeout(() => { - appendMessage(data.reply, 'bot'); - }, 500); - } catch (error) { - console.error('Error:', error); - appendMessage("Sorry, something went wrong. Please try again.", 'bot'); - } + // Basic interaction for toasts + const toasts = document.querySelectorAll('.toast'); + toasts.forEach(toastEl => { + const toast = new bootstrap.Toast(toastEl, { delay: 5000 }); + toast.show(); }); -}); + + // Lazy load or pre-fetch images if needed + console.log('ApkNusa ready.'); +}); \ No newline at end of file diff --git a/index.php b/index.php index 7205f3d..8c5e355 100644 --- a/index.php +++ b/index.php @@ -1,150 +1,32 @@ - - - - - - New Style - - - - - - - - - - - - - - - - - - - - - -
-
-

Analyzing your requirements and generating your website…

-
- Loading… -
-

AI is collecting your requirements and applying the first changes.

-

This page will update automatically as the plan is implemented.

-

Runtime: PHP — UTC

-
-
- - - +}); + +use App\Core\Router; + +$router = new Router(); + +// Routes +$router->add('GET', '/', ['App\Controllers\HomeController', 'index']); +$router->add('GET', '/apk/{slug}', ['App\Controllers\ApkController', 'detail']); +$router->add('GET', '/apk/{slug}/download', ['App\Controllers\ApkController', 'download']); + +$method = $_SERVER['REQUEST_METHOD']; +$uri = $_SERVER['REQUEST_URI']; + +$router->dispatch($method, $uri); \ No newline at end of file diff --git a/views/apk_detail.php b/views/apk_detail.php new file mode 100644 index 0000000..3d204b6 --- /dev/null +++ b/views/apk_detail.php @@ -0,0 +1,130 @@ + + +
+ + +
+
+
+
+ <?php echo $apk['title']; ?> +
+

v

+

Official and original version. Verified safe for Android device.

+
+ Downloads + Verified Safe + + + VIP + +
+ + Download APK Now + +

File size: ~45MB (approx.) | Android 6.0 or higher

+
+
+ +
+

Description

+

+
+ +
+
+
+
Main Features
+
    +
  • Original APK from developer
  • +
  • No extra files needed
  • +
  • Fast and direct download
  • +
  • Regular updates included
  • +
+
+
+
+
+
System Requirements
+
    +
  • Android 6.0+ (Marshmallow)
  • +
  • 2GB RAM minimum recommended
  • +
  • Stable internet connection
  • +
  • ARMv8 or newer processor
  • +
+
+
+
+ +
+
Is this safe to download?
+

Yes, every APK on ApkNusa is scanned for malware and verified to ensure it is the original, unmodified file from the developer.

+
+
+
+ +
+
+
+
Popular Similar Apps
+
+
+ Facebook +
+
Facebook
+ Social Media +
+ View +
+
+ TikTok +
+
TikTok
+ Entertainment +
+ View +
+
+ YouTube +
+
YouTube
+ Video Player +
+ View +
+
+
+ See all Apps +
+ +
+ +
Join our community
+

Register today to enjoy premium features, earn rewards, and track your download history.

+ Join Now +
+
+
+
+
+ + +
+ +
+ + + diff --git a/views/footer.php b/views/footer.php new file mode 100644 index 0000000..a21e1d9 --- /dev/null +++ b/views/footer.php @@ -0,0 +1,57 @@ + + + + + + diff --git a/views/header.php b/views/header.php new file mode 100644 index 0000000..ece91a6 --- /dev/null +++ b/views/header.php @@ -0,0 +1,48 @@ + + + + + + <?php echo $title ?? 'ApkNusa'; ?> + + + + + + + + + + +
diff --git a/views/home.php b/views/home.php new file mode 100644 index 0000000..27bf778 --- /dev/null +++ b/views/home.php @@ -0,0 +1,66 @@ + + +
+
+
+

Download the Best Android APKs Professionally

+

Fast, safe, and secure downloads for your favorite mobile apps and games. No registration required to browse.

+ +
+
+
+
+ Android APKs +
+
+
+ +
+
+

Latest Releases

+ View All +
+
+ +
+
+
+
+ <?php echo $apk['title']; ?> +
+
+ v + + VIP + +
+
+

+
+ + Details +
+
+
+
+ +
+
+ +
+
+
+

Start your referral journey today

+

Join our community, share your favorite APKs, and earn reward points for every successful referral download.

+
+ +
+
+
+ +