summaryrefslogtreecommitdiff
path: root/src/web/core/router.php
diff options
context:
space:
mode:
Diffstat (limited to 'src/web/core/router.php')
-rw-r--r--src/web/core/router.php248
1 files changed, 248 insertions, 0 deletions
diff --git a/src/web/core/router.php b/src/web/core/router.php
new file mode 100644
index 0000000..c8fb142
--- /dev/null
+++ b/src/web/core/router.php
@@ -0,0 +1,248 @@
+<?php /* Copyright (c) 2024 Freya Murphy */
+class Router {
+
+ // the loader
+ private $load;
+
+ // the main model
+ private $main;
+
+ // the database
+ private $db;
+
+ private $db_ready;
+
+ /**
+ * Creates a router
+ * @param Loader $load - the main laoder object
+ */
+ function __construct($load) {
+ $this->load = $load;
+ $this->db = $load->db();
+ $this->main = $this->load->model('main');
+ $this->db_ready = file_exists('/status/ready');
+ }
+
+ /**
+ * @param string $path - the current request path
+ * Gets the current route
+ * @return array<string,mixed>
+ */
+ private function get_req_route($path): array {
+ // trim the path
+ $path = trim($path);
+ // remove first '/'
+ $path = substr($path, 1);
+ // get path parts
+ $parts = explode('/', $path);
+
+ $len = count($parts);
+
+ // get route info
+ $route = array();
+ // e.g. /
+ if ($path === '') {
+ $route = array(
+ 'app' => '',
+ 'slug' => 'index',
+ );
+ // e.g. /home /login
+ } else if ($len === 1) {
+ $route = array(
+ 'app' => $parts[0],
+ 'slug' => 'index',
+ );
+ // e.g. /home/posts
+ } else {
+ $route = array (
+ 'app' => implode('/', array_slice($parts, 0, -1)),
+ 'slug' => end($parts)
+ );
+ };
+
+ $routes = $GLOBALS['routes'];
+ if (array_key_exists($route['app'], $routes)) {
+ $parts = explode('/', $routes[$route['app']]);
+ if (count($parts) == 1) {
+ $route['app'] = $parts[0];
+ } else {
+ $route['app'] = $parts[0];
+ $route['slug'] = $parts[1];
+ }
+ }
+
+ return $route;
+ }
+
+ /**
+ * Gets the users ip
+ */
+ private function get_ip(): string {
+ $ip = '';
+ if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
+ $ip = $_SERVER['HTTP_CLIENT_IP'];
+ } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
+ $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
+ } else {
+ $ip = $_SERVER['REMOTE_ADDR'];
+ }
+ return $ip;
+ }
+
+ /**
+ * Gets the curret request info
+ * @return array<string,mixed>
+ */
+ private function get_req(): array|bool {
+ $method = $_SERVER['REQUEST_METHOD'];
+
+ $uri_str = $_SERVER['REQUEST_URI'];
+ $uri = parse_url($uri_str);
+ if (!$uri) {
+ return FALSE;
+ }
+
+ $path = '';
+ if (array_key_exists('path', $uri)) {
+ $path = $uri['path'];
+ }
+
+ return array_merge(
+ array(
+ 'uri' => $uri,
+ 'uri_str' => $uri_str,
+ 'method' => $method,
+ 'ip' => $this->get_ip()
+ ),
+ $this->get_req_route($path),
+ );
+ }
+
+ /**
+ * Handles a router error code
+ * @param int $code - the http error code
+ * @param bool $recursed
+ */
+ private function handle_error($code, $recursed): void {
+ if ($recursed) {
+ die($code . ' (recursed)');
+ }
+ $uri_str = $_SERVER['REQUEST_URI'];
+ $req = array();
+ $req['slug'] = 'index';
+ $req['app'] = 'error';
+ $req['uri_str'] = $uri_str;
+ $this->main->info = $req;
+ $_GET['code'] = $code;
+ $this->handle_req($req, TRUE);
+ }
+
+ private function load_htc($req, $recursed): void {
+ $parts = explode('/', $req['uri_str']);
+ $file = end($parts);
+ $path = $GLOBALS['publicroot'] . '/polyfills/' . $file;
+
+ if (file_exists($path)) {
+ header('Content-type: text/x-component');
+ include($path);
+ } else {
+ $this->handle_error(400, $recursed);
+ }
+ }
+
+ /**
+ * @param array $req
+ * @param bool $recursed
+ */
+ private function handle_req($req, $recursed = FALSE): void {
+
+ if ($recursed === false) {
+ if (
+ $this->db_ready === false &&
+ in_array($req['app'], $GLOBALS['serviceable']) === false
+ ) {
+ $this->handle_error(503, $recursed);
+ return;
+ }
+
+ if ($this->check_banned($req)) {
+ $this->handle_error(401, $recursed);
+ return;
+ }
+ }
+
+ if (!$req) {
+ $this->handle_error(500, $recursed);
+ return;
+ }
+
+ if (str_ends_with($req['uri_str'], '.htc')) {
+ $this->load_htc($req, $recursed);
+ return;
+ }
+
+ $controller = $this->load->controller($req['app']);
+
+ if ($controller === NULL) {
+ $this->handle_error(404, $recursed);
+ return;
+ }
+
+ $ref = NULL;
+ try {
+ $ref = new ReflectionMethod($controller, $req['slug']);
+ } catch (Exception $_e) {}
+
+ if ($ref === NULL || !$ref->isPublic()) {
+ $this->handle_error(404, $recursed);
+ return;
+
+ }
+
+ $ref->invoke($controller);
+ }
+
+ private function log_request($req): void {
+ if (
+ $req === FALSE ||
+ $this->db_ready === FALSE ||
+ in_array($req['app'], $GLOBALS['serviceable'])
+ ) {
+ return;
+ }
+
+ $query = $this->db
+ ->insert_into('admin.request_log',
+ 'ip', 'method', 'uri')
+ ->values(
+ $req['ip'], $req['method'], $req['uri_str']);
+
+ $query->execute();
+ }
+
+ private function check_banned($req) {
+ $ip = FALSE;
+ if ($req) {
+ $ip = $req['ip'];
+ } else {
+ $ip = $this->get_ip();
+ }
+ $query = $this->db
+ ->select('TRUE')
+ ->from('admin.banned')
+ ->where('ip')->eq($ip);
+
+ return !!($query->row());
+ }
+
+ /**
+ * Handels the incomming reuqest
+ */
+ public function handle_request(): void {
+ $req = $this->get_req();
+ $this->log_request($req);
+ $this->main->info = $req;
+ $this->handle_req($req);
+ }
+
+}