diff --git a/app/Controllers/AdminController.php b/app/Controllers/AdminController.php
new file mode 100644
index 0000000..a9aecd0
--- /dev/null
+++ b/app/Controllers/AdminController.php
@@ -0,0 +1,239 @@
+redirect('/admin/login');
+ }
+ }
+
+ public function loginForm() {
+ if (isset($_SESSION['user_id'])) {
+ $this->redirect('/admin/dashboard');
+ }
+ $this->view('admin/login');
+ }
+
+ public function login() {
+ $username = $_POST['username'] ?? '';
+ $password = $_POST['password'] ?? '';
+
+ $db = db_pdo();
+ $stmt = $db->prepare("SELECT * FROM users WHERE username = ?");
+ $stmt->execute([$username]);
+ $user = $stmt->fetch();
+
+ if ($user && password_verify($password, $user['password'])) {
+ $_SESSION['user_id'] = $user['id'];
+ $_SESSION['username'] = $user['username'];
+ $this->redirect('/admin/dashboard');
+ } else {
+ $error = "Invalid username or password";
+ $this->view('admin/login', ['error' => $error]);
+ }
+ }
+
+ public function logout() {
+ session_destroy();
+ $this->redirect('/admin/login');
+ }
+
+ public function dashboard() {
+ $this->checkAuth();
+ $apkService = new ApkService();
+ $db = db_pdo();
+ $stats = [
+ 'total_apks' => count($apkService->getAllApks()),
+ 'total_downloads' => $this->getTotalDownloads(),
+ 'total_users' => $db->query("SELECT COUNT(*) FROM users")->fetchColumn(),
+ 'pending_withdrawals' => $db->query("SELECT COUNT(*) FROM withdrawals WHERE status = 'pending'")->fetchColumn(),
+ 'recent_apks' => array_slice($apkService->getAllApks(), 0, 5)
+ ];
+ $this->view('admin/dashboard', $stats);
+ }
+
+ private function getTotalDownloads() {
+ $db = db_pdo();
+ return $db->query("SELECT SUM(total_downloads) FROM apks")->fetchColumn() ?: 0;
+ }
+
+ // APK Management
+ public function apks() {
+ $this->checkAuth();
+ $apkService = new ApkService();
+ $apks = $apkService->getAllApks();
+ $this->view('admin/apks/index', ['apks' => $apks]);
+ }
+
+ public function addApkForm() {
+ $this->checkAuth();
+ $db = db_pdo();
+ $categories = $db->query("SELECT * FROM categories")->fetchAll();
+ $this->view('admin/apks/form', ['action' => 'add', 'categories' => $categories]);
+ }
+
+ public function addApk() {
+ $this->checkAuth();
+ $title = $_POST['title'];
+ $slug = $this->slugify($title);
+ $description = $_POST['description'];
+ $version = $_POST['version'];
+ $image_url = $_POST['image_url'];
+ $download_url = $_POST['download_url'];
+ $category_id = $_POST['category_id'] ?? null;
+ $status = $_POST['status'] ?? 'published';
+ $is_vip = isset($_POST['is_vip']) ? 1 : 0;
+
+ $icon_path = $this->handleUpload('icon_file');
+
+ $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)");
+ $stmt->execute([$title, $slug, $description, $version, $image_url, $icon_path, $download_url, $category_id, $status, $is_vip]);
+
+ $this->redirect('/admin/apks');
+ }
+
+ public function editApkForm($params) {
+ $this->checkAuth();
+ $apkService = new ApkService();
+ $apk = $apkService->getApkById($params['id']);
+ $db = db_pdo();
+ $categories = $db->query("SELECT * FROM categories")->fetchAll();
+ $this->view('admin/apks/form', ['action' => 'edit', 'apk' => $apk, 'categories' => $categories]);
+ }
+
+ public function editApk($params) {
+ $this->checkAuth();
+ $title = $_POST['title'];
+ $description = $_POST['description'];
+ $version = $_POST['version'];
+ $image_url = $_POST['image_url'];
+ $download_url = $_POST['download_url'];
+ $category_id = $_POST['category_id'] ?? null;
+ $status = $_POST['status'];
+ $is_vip = isset($_POST['is_vip']) ? 1 : 0;
+
+ $db = db_pdo();
+ $apk = $db->query("SELECT * FROM apks WHERE id = " . $params['id'])->fetch();
+ $icon_path = $this->handleUpload('icon_file') ?: $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']]);
+
+ $this->redirect('/admin/apks');
+ }
+
+ public function updateOrder() {
+ $this->checkAuth();
+ $order = $_POST['order'] ?? [];
+ $db = db_pdo();
+ foreach ($order as $index => $id) {
+ $stmt = $db->prepare("UPDATE apks SET display_order = ? WHERE id = ?");
+ $stmt->execute([$index, $id]);
+ }
+ header('Content-Type: application/json');
+ echo json_encode(['success' => true]);
+ }
+
+ private function handleUpload($field) {
+ if (!isset($_FILES[$field]) || $_FILES[$field]['error'] !== UPLOAD_ERR_OK) {
+ return null;
+ }
+
+ $uploadDir = 'assets/uploads/icons/';
+ if (!is_dir($uploadDir)) {
+ mkdir($uploadDir, 0775, true);
+ }
+
+ $ext = pathinfo($_FILES[$field]['name'], PATHINFO_EXTENSION);
+ $fileName = uniqid() . '.' . $ext;
+ $targetPath = $uploadDir . $fileName;
+
+ if (move_uploaded_file($_FILES[$field]['tmp_name'], $targetPath)) {
+ return $targetPath;
+ }
+
+ return null;
+ }
+
+ // Category Management
+ public function categories() {
+ $this->checkAuth();
+ $db = db_pdo();
+ $categories = $db->query("SELECT * FROM categories")->fetchAll();
+ $this->view('admin/categories/index', ['categories' => $categories]);
+ }
+
+ public function addCategory() {
+ $this->checkAuth();
+ $name = $_POST['name'];
+ $slug = $this->slugify($name);
+ $db = db_pdo();
+ $stmt = $db->prepare("INSERT INTO categories (name, slug) VALUES (?, ?)");
+ $stmt->execute([$name, $slug]);
+ $this->redirect('/admin/categories');
+ }
+
+ public function deleteCategory($params) {
+ $this->checkAuth();
+ $db = db_pdo();
+ $stmt = $db->prepare("DELETE FROM categories WHERE id = ?");
+ $stmt->execute([$params['id']]);
+ $this->redirect('/admin/categories');
+ }
+
+ // Withdrawal Management
+ public function withdrawals() {
+ $this->checkAuth();
+ $db = db_pdo();
+ $withdrawals = $db->query("SELECT w.*, u.username FROM withdrawals w JOIN users u ON w.user_id = u.id ORDER BY w.created_at DESC")->fetchAll();
+ $this->view('admin/withdrawals/index', ['withdrawals' => $withdrawals]);
+ }
+
+ public function approveWithdrawal($params) {
+ $this->checkAuth();
+ $db = db_pdo();
+ $stmt = $db->prepare("UPDATE withdrawals SET status = 'approved' WHERE id = ?");
+ $stmt->execute([$params['id']]);
+ $this->redirect('/admin/withdrawals');
+ }
+
+ 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 = ?");
+ $stmt->execute([$wd['amount'], $wd['user_id']]);
+
+ $stmt = $db->prepare("UPDATE withdrawals SET status = 'rejected' WHERE id = ?");
+ $stmt->execute([$params['id']]);
+ }
+ $this->redirect('/admin/withdrawals');
+ }
+
+ public function deleteApk($params) {
+ $this->checkAuth();
+ $db = db_pdo();
+ $stmt = $db->prepare("DELETE FROM apks WHERE id = ?");
+ $stmt->execute([$params['id']]);
+ $this->redirect('/admin/apks');
+ }
+
+ private function slugify($text) {
+ $text = preg_replace('~[^\pL\d]+~u', '-', $text);
+ $text = iconv('utf-8', 'us-ascii//TRANSLIT', $text);
+ $text = preg_replace('~[^-\w]+~', '', $text);
+ $text = trim($text, '-');
+ $text = preg_replace('~-+~', '-', $text);
+ $text = strtolower($text);
+ return empty($text) ? 'n-a' : $text;
+ }
+}
\ No newline at end of file
diff --git a/app/Controllers/ApkController.php b/app/Controllers/ApkController.php
index ad1aca3..2cca845 100644
--- a/app/Controllers/ApkController.php
+++ b/app/Controllers/ApkController.php
@@ -28,11 +28,26 @@ class ApkController extends Controller {
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']);
+
+ if (!$apk) {
+ header("HTTP/1.0 404 Not Found");
+ echo "APK Not Found";
+ return;
}
+
+ // Increment download counter
+ $this->apkService->incrementDownload($apk['id']);
+
+ // Get the download URL
+ $downloadUrl = $apk['download_url'];
+
+ // If URL is empty or #, redirect back to detail with a message
+ if (empty($downloadUrl) || $downloadUrl === '#') {
+ $this->redirect('/apk/' . $apk['slug'] . '?error=no_url');
+ return;
+ }
+
+ // Redirect to the actual download link (External URL)
+ $this->redirect($downloadUrl);
}
-}
+}
\ No newline at end of file
diff --git a/app/Controllers/AuthController.php b/app/Controllers/AuthController.php
new file mode 100644
index 0000000..c20a9b1
--- /dev/null
+++ b/app/Controllers/AuthController.php
@@ -0,0 +1,157 @@
+redirect('/profile');
+ }
+ $this->view('auth/login');
+ }
+
+ public function registerForm() {
+ if (isset($_SESSION['user_id'])) {
+ $this->redirect('/profile');
+ }
+ $ref = $_GET['ref'] ?? '';
+ $this->view('auth/register', ['ref' => $ref]);
+ }
+
+ public function login() {
+ $username = $_POST['username'] ?? '';
+ $password = $_POST['password'] ?? '';
+
+ $db = db_pdo();
+ $stmt = $db->prepare("SELECT * FROM users WHERE username = ? AND role = 'user'");
+ $stmt->execute([$username]);
+ $user = $stmt->fetch();
+
+ if ($user && password_verify($password, $user['password'])) {
+ $_SESSION['user_id'] = $user['id'];
+ $_SESSION['username'] = $user['username'];
+ $_SESSION['role'] = $user['role'];
+ $this->redirect('/profile');
+ } else {
+ $this->view('auth/login', ['error' => 'Invalid username or password']);
+ }
+ }
+
+ public function register() {
+ $username = $_POST['username'] ?? '';
+ $password = $_POST['password'] ?? '';
+ $confirm_password = $_POST['confirm_password'] ?? '';
+ $ref_code = $_POST['ref_code'] ?? '';
+
+ if ($password !== $confirm_password) {
+ $this->view('auth/register', ['error' => 'Passwords do not match', 'ref' => $ref_code]);
+ return;
+ }
+
+ $db = db_pdo();
+
+ // Check if username exists
+ $stmt = $db->prepare("SELECT id FROM users WHERE username = ?");
+ $stmt->execute([$username]);
+ if ($stmt->fetch()) {
+ $this->view('auth/register', ['error' => 'Username already exists', 'ref' => $ref_code]);
+ return;
+ }
+
+ $hashed_password = password_hash($password, PASSWORD_DEFAULT);
+ $referral_code = substr(md5(uniqid($username, true)), 0, 8);
+
+ $referred_by = null;
+ if (!empty($ref_code)) {
+ $stmt = $db->prepare("SELECT id FROM users WHERE referral_code = ?");
+ $stmt->execute([$ref_code]);
+ $referrer = $stmt->fetch();
+ if ($referrer) {
+ $referred_by = $referrer['id'];
+ }
+ }
+
+ $stmt = $db->prepare("INSERT INTO users (username, password, referral_code, referred_by, role, balance) VALUES (?, ?, ?, ?, 'user', 0)");
+ $stmt->execute([$username, $hashed_password, $referral_code, $referred_by]);
+ $userId = $db->lastInsertId();
+
+ if ($referred_by) {
+ // Reward referrer with points (not balance yet, as per previous logic)
+ $stmt = $db->prepare("UPDATE users SET points = points + 10, total_referrals = total_referrals + 1 WHERE id = ?");
+ $stmt->execute([$referred_by]);
+ }
+
+ $_SESSION['user_id'] = $userId;
+ $_SESSION['username'] = $username;
+ $_SESSION['role'] = 'user';
+
+ $this->redirect('/profile');
+ }
+
+ public function logout() {
+ session_destroy();
+ $this->redirect('/');
+ }
+
+ public function profile() {
+ if (!isset($_SESSION['user_id'])) {
+ $this->redirect('/login');
+ }
+
+ $db = db_pdo();
+ $stmt = $db->prepare("SELECT * FROM users WHERE id = ?");
+ $stmt->execute([$_SESSION['user_id']]);
+ $user = $stmt->fetch();
+
+ $stmt = $db->prepare("SELECT * FROM withdrawals WHERE user_id = ? ORDER BY created_at DESC");
+ $stmt->execute([$user['id']]);
+ $withdrawals = $stmt->fetchAll();
+
+ $this->view('auth/profile', [
+ 'user' => $user,
+ 'withdrawals' => $withdrawals,
+ 'success' => $_SESSION['success'] ?? null,
+ 'error' => $_SESSION['error'] ?? null
+ ]);
+ unset($_SESSION['success'], $_SESSION['error']);
+ }
+
+ public function requestWithdrawal() {
+ if (!isset($_SESSION['user_id'])) {
+ $this->redirect('/login');
+ }
+
+ $amount = (float)$_POST['amount'];
+ $method = $_POST['method'];
+ $details = $_POST['details'];
+
+ if ($amount < 10000) { // Minimum WD
+ $_SESSION['error'] = "Minimum withdrawal is Rp 10.000";
+ $this->redirect('/profile');
+ }
+
+ $db = db_pdo();
+ $stmt = $db->prepare("SELECT balance FROM users WHERE id = ?");
+ $stmt->execute([$_SESSION['user_id']]);
+ $balance = $stmt->fetchColumn();
+
+ if ($balance < $amount) {
+ $_SESSION['error'] = "Insufficient balance";
+ $this->redirect('/profile');
+ }
+
+ // Deduct balance
+ $stmt = $db->prepare("UPDATE users SET balance = balance - ? WHERE id = ?");
+ $stmt->execute([$amount, $_SESSION['user_id']]);
+
+ // Create WD request
+ $stmt = $db->prepare("INSERT INTO withdrawals (user_id, amount, method, account_details, status) VALUES (?, ?, ?, ?, 'pending')");
+ $stmt->execute([$_SESSION['user_id'], $amount, $method, $details]);
+
+ $_SESSION['success'] = "Withdrawal request submitted successfully";
+ $this->redirect('/profile');
+ }
+}
\ No newline at end of file
diff --git a/app/Controllers/HomeController.php b/app/Controllers/HomeController.php
index aa23f18..78eb02b 100644
--- a/app/Controllers/HomeController.php
+++ b/app/Controllers/HomeController.php
@@ -13,10 +13,93 @@ class HomeController extends Controller {
}
public function index() {
- $apks = $this->apkService->getLatest(12);
+ $db = db_pdo();
+ $category = $_GET['category'] ?? null;
+
+ $sql = "SELECT * FROM apks WHERE status = 'published'";
+ $params = [];
+
+ if ($category) {
+ $sql .= " AND category_id = (SELECT id FROM categories WHERE slug = ?)";
+ $params[] = $category;
+ }
+
+ $sql .= " ORDER BY display_order ASC, created_at DESC LIMIT 12";
+
+ $stmt = $db->prepare($sql);
+ $stmt->execute($params);
+ $apks = $stmt->fetchAll();
+
return $this->view('home', [
'apks' => $apks,
'title' => 'ApkNusa - Professional APK Download Portal'
]);
}
-}
+
+ public function apkDetail($params) {
+ $slug = $params['slug'];
+ $db = db_pdo();
+ $stmt = $db->prepare("SELECT * FROM apks WHERE slug = ?");
+ $stmt->execute([$slug]);
+ $apk = $stmt->fetch();
+
+ if (!$apk) {
+ $this->redirect('/');
+ }
+
+ // Store referral code if present
+ if (isset($_GET['ref'])) {
+ $_SESSION['ref_download_' . $apk['id']] = $_GET['ref'];
+ }
+
+ $this->view('apk_detail', ['apk' => $apk]);
+ }
+
+ public function download($params) {
+ $slug = $params['slug'];
+ $db = db_pdo();
+ $stmt = $db->prepare("SELECT * FROM apks WHERE slug = ?");
+ $stmt->execute([$slug]);
+ $apk = $stmt->fetch();
+
+ if (!$apk) {
+ $this->redirect('/');
+ }
+
+ // Check for referral earnings
+ $ref_code = $_SESSION['ref_download_' . $apk['id']] ?? null;
+ if ($ref_code) {
+ $stmt = $db->prepare("SELECT id FROM users WHERE referral_code = ?");
+ $stmt->execute([$ref_code]);
+ $referrer = $stmt->fetch();
+
+ if ($referrer) {
+ $referrer_id = $referrer['id'];
+ $ip = $_SERVER['REMOTE_ADDR'];
+
+ // Check if this IP already earned for this APK today (prevent abuse)
+ $stmt = $db->prepare("SELECT id FROM referral_downloads WHERE referrer_id = ? AND apk_id = ? AND ip_address = ? AND created_at > DATE_SUB(NOW(), INTERVAL 1 DAY)");
+ $stmt->execute([$referrer_id, $apk['id'], $ip]);
+
+ if (!$stmt->fetch()) {
+ // Credit 500 IDR
+ $stmt = $db->prepare("UPDATE users SET balance = balance + 500 WHERE id = ?");
+ $stmt->execute([$referrer_id]);
+
+ // Log download
+ $stmt = $db->prepare("INSERT INTO referral_downloads (referrer_id, apk_id, ip_address, amount) VALUES (?, ?, ?, 500)");
+ $stmt->execute([$referrer_id, $apk['id'], $ip]);
+ }
+ }
+ // Clear session after processing
+ unset($_SESSION['ref_download_' . $apk['id']]);
+ }
+
+ // Increment total downloads
+ $stmt = $db->prepare("UPDATE apks SET total_downloads = total_downloads + 1 WHERE id = ?");
+ $stmt->execute([$apk['id']]);
+
+ // Redirect to actual file
+ $this->redirect($apk['download_url']);
+ }
+}
\ No newline at end of file
diff --git a/app/Core/Router.php b/app/Core/Router.php
index cf1ee12..80a0c88 100644
--- a/app/Core/Router.php
+++ b/app/Core/Router.php
@@ -3,36 +3,49 @@
namespace App\Core;
class Router {
- protected $routes = [];
+ private $routes = [];
- public function add($method, $path, $handler) {
- $path = preg_replace('/\{([a-z]+)\}/', '(?P<\1>[^/]+)', $path);
+ public function get($path, $handler) {
+ $this->add('GET', $path, $handler);
+ }
+
+ public function post($path, $handler) {
+ $this->add('POST', $path, $handler);
+ }
+
+ private function add($method, $path, $handler) {
+ $path = preg_replace('/:([^\/]+)/', '(?P<$1>[^/]+)', $path);
+ $path = '#^' . $path . '$#';
$this->routes[] = [
- 'method' => strtoupper($method),
- 'path' => '#^' . $path . '$#',
+ 'method' => $method,
+ 'path' => $path,
'handler' => $handler
];
}
- public function dispatch($method, $uri) {
- $uri = parse_url($uri, PHP_URL_PATH);
-
+ public function dispatch() {
+ $uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
+ $method = $_SERVER['REQUEST_METHOD'];
+
foreach ($this->routes as $route) {
- if ($route['method'] === strtoupper($method) && preg_match($route['path'], $uri, $matches)) {
+ if ($route['method'] === $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;
+
+ if (is_string($handler) && strpos($handler, '@') !== false) {
+ [$controllerName, $methodName] = explode('@', $handler);
+ $controllerClass = "App\\Controllers\\".$controllerName;
$controller = new $controllerClass();
return $controller->$methodName($params);
}
-
- return $handler($params);
+
+ if (is_callable($handler)) {
+ return call_user_func_array($handler, [$params]);
+ }
}
}
header("HTTP/1.0 404 Not Found");
echo "404 Not Found";
}
-}
+}
\ No newline at end of file
diff --git a/app/Helpers/functions.php b/app/Helpers/functions.php
new file mode 100644
index 0000000..446ffe1
--- /dev/null
+++ b/app/Helpers/functions.php
@@ -0,0 +1,16 @@
+fetch();
}
+ public function getAllApks() {
+ $stmt = $this->db->prepare("SELECT * FROM apks ORDER BY created_at DESC");
+ $stmt->execute();
+ return $stmt->fetchAll();
+ }
+
+ public function getApkById($id) {
+ $stmt = $this->db->prepare("SELECT * FROM apks WHERE id = ?");
+ $stmt->execute([$id]);
+ 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]);
@@ -34,4 +46,4 @@ class ApkService {
'ip' => $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0'
]);
}
-}
+}
\ No newline at end of file
diff --git a/app/Services/LanguageService.php b/app/Services/LanguageService.php
new file mode 100644
index 0000000..67d0c07
--- /dev/null
+++ b/app/Services/LanguageService.php
@@ -0,0 +1,10 @@
+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']);
+// Home & APKs
+$router->get('/', 'HomeController@index');
+$router->get('/apk/:slug', 'HomeController@apkDetail');
+$router->get('/download/:slug', 'HomeController@download');
-$method = $_SERVER['REQUEST_METHOD'];
-$uri = $_SERVER['REQUEST_URI'];
+// Auth
+$router->get('/login', 'AuthController@loginForm');
+$router->post('/login', 'AuthController@login');
+$router->get('/register', 'AuthController@registerForm');
+$router->post('/register', 'AuthController@register');
+$router->get('/logout', 'AuthController@logout');
+$router->get('/profile', 'AuthController@profile');
+$router->post('/withdraw', 'AuthController@requestWithdrawal');
-$router->dispatch($method, $uri);
\ No newline at end of file
+// Admin Auth
+$router->get('/admin/login', 'AdminController@loginForm');
+$router->post('/admin/login', 'AdminController@login');
+$router->get('/admin/logout', 'AdminController@logout');
+
+// Admin Dashboard
+$router->get('/admin/dashboard', 'AdminController@dashboard');
+
+// Admin APKs
+$router->get('/admin/apks', 'AdminController@apks');
+$router->get('/admin/apks/add', 'AdminController@addApkForm');
+$router->post('/admin/apks/add', 'AdminController@addApk');
+$router->get('/admin/apks/edit/:id', 'AdminController@editApkForm');
+$router->post('/admin/apks/edit/:id', 'AdminController@editApk');
+$router->get('/admin/apks/delete/:id', 'AdminController@deleteApk');
+$router->post('/admin/apks/reorder', 'AdminController@updateOrder');
+
+// Admin Categories
+$router->get('/admin/categories', 'AdminController@categories');
+$router->post('/admin/categories/add', 'AdminController@addCategory');
+$router->get('/admin/categories/delete/:id', 'AdminController@deleteCategory');
+
+// Admin Withdrawals
+$router->get('/admin/withdrawals', 'AdminController@withdrawals');
+$router->get('/admin/withdrawals/approve/:id', 'AdminController@approveWithdrawal');
+$router->get('/admin/withdrawals/reject/:id', 'AdminController@rejectWithdrawal');
+
+$router->dispatch();
\ No newline at end of file
diff --git a/views/admin/apks/form.php b/views/admin/apks/form.php
new file mode 100644
index 0000000..d2adf5a
--- /dev/null
+++ b/views/admin/apks/form.php
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/views/admin/apks/index.php b/views/admin/apks/index.php
new file mode 100644
index 0000000..8f5fe68
--- /dev/null
+++ b/views/admin/apks/index.php
@@ -0,0 +1,116 @@
+
+
+