diff options
author | Freya Murphy <freya@freyacat.org> | 2024-03-29 22:29:56 -0400 |
---|---|---|
committer | Freya Murphy <freya@freyacat.org> | 2024-03-29 22:29:56 -0400 |
commit | 944b6b0526032ad8c1b4a2612d6723bec75e0e4c (patch) | |
tree | d3da5584df33a7878c087622b4fc2ec2883cf880 /web/core | |
download | xssbook2-944b6b0526032ad8c1b4a2612d6723bec75e0e4c.tar.gz xssbook2-944b6b0526032ad8c1b4a2612d6723bec75e0e4c.tar.bz2 xssbook2-944b6b0526032ad8c1b4a2612d6723bec75e0e4c.zip |
start database (user and post), and initial barebones home page
Diffstat (limited to 'web/core')
-rw-r--r-- | web/core/aesthetic.php | 55 | ||||
-rw-r--r-- | web/core/controller.php | 55 | ||||
-rw-r--r-- | web/core/database.php | 173 | ||||
-rw-r--r-- | web/core/error.php | 12 | ||||
-rw-r--r-- | web/core/helper.php | 0 | ||||
-rw-r--r-- | web/core/loader.php | 38 | ||||
-rw-r--r-- | web/core/main.php | 123 | ||||
-rw-r--r-- | web/core/model.php | 29 | ||||
-rw-r--r-- | web/core/router.php | 127 |
9 files changed, 612 insertions, 0 deletions
diff --git a/web/core/aesthetic.php b/web/core/aesthetic.php new file mode 100644 index 0000000..1180ad1 --- /dev/null +++ b/web/core/aesthetic.php @@ -0,0 +1,55 @@ +<?php /* Copyright (c) 2024 Freya Murphy */ +class Aesthetic { + + private $config; + + function __construct() { + $this->config = array( + '_common' => array( + 'js' => [ + 'js/jquery-3.7.1.min.js', + 'js/lib.js', + 'js/modal.js', + ], + 'css' => [ + 'css/common.css' + ], + ), + 'error' => array( + 'css' => [ + 'css/error.css' + ], + ), + 'home' => array( + 'js' => [ + 'js/post.js', + ], + 'css' => [ + 'css/home.css', + 'css/post.css' + ], + ), + ); + } + + function get_files($route) { + $js_files = $this->config['_common']['js']; + $css_files = $this->config['_common']['css']; + + if (array_key_exists($route, $this->config)) { + $config = $this->config[$route]; + if (array_key_exists('js', $config)) { + $js_files = array_merge($js_files, $config['js']); + } + if (array_key_exists('css', $config)) { + $css_files = array_merge($css_files, $config['css']); + } + } + + return array( + 'js_files' => $js_files, + 'css_files' => $css_files, + ); + } + +} diff --git a/web/core/controller.php b/web/core/controller.php new file mode 100644 index 0000000..946b460 --- /dev/null +++ b/web/core/controller.php @@ -0,0 +1,55 @@ +<?php /* Copyright (c) 2024 Freya Murphy */ +abstract class Controller { + + // the main model + public $main; + + // the loader + public $load; + + // the database + public $db; + + function __construct() { + $this->main = $GLOBALS['__vars']['main']; + $this->load = $GLOBALS['__vars']['load']; + $this->db = $this->main->db; + + $info = $this->main->info; + $lang_code = $info['lang']; + $route_name = $info['route']; + $this->load->lang($lang_code); + $this->load->route_lang($lang_code, $route_name); + } + + public function index() {} + + protected function view($__name, $data = array()) { + $__root = $GLOBALS['webroot']; + $__path = $__root . '/views/' . $__name . '.php'; + if (is_file($__path)) { + extract($data); + require($__path); + return; + } + } + + protected function app_view($__name, $data = array()) { + $__root = $GLOBALS['webroot']; + $__route = $this->main->info['route']; + $__path = $__root . '/routes/' . $__route . '/views/' . $__name . '.php'; + if (is_file($__path)) { + extract($data); + require($__path); + return; + } + } + + protected function modal($title, $content, $data = array()) { + $data['title'] = $title; + $data['content'] = $content; + $this->view('template/modal', $data); + } + +} +?> diff --git a/web/core/database.php b/web/core/database.php new file mode 100644 index 0000000..b3a597b --- /dev/null +++ b/web/core/database.php @@ -0,0 +1,173 @@ +<?php /* Copyright (c) 2024 Freya Murphy */ + +class DatabaseQuery { + + private $conn; + private $query; + + private $where; + private $set; + + private $param; + + function __construct($conn) { + $this->conn = $conn; + $this->query = ''; + + $this->set = FALSE; + $this->where = FALSE; + $this->param = array(); + } + + private function in($array) { + $in = 'IN ('; + foreach ($array as $idx => $item) { + if ($idx != 0) { + $in .= ","; + } + $in .= "?"; + array_push($this->param, $item); + } + $in .= ")"; + return $in; + } + + public function select($select) { + $this->query .= "SELECT $select\n"; + return $this; + } + + public function from($from) { + $this->query .= "FROM $from\n"; + return $this; + } + + public function where($cond) { + if (!$this->where) { + $this->where = TRUE; + $this->query .= "WHERE "; + } + $this->query .= "$cond "; + return $this; + } + + public function like($item) { + $this->query .= "LIKE ?\n"; + array_push($this->param, $item); + return $this; + } + + public function eq($item) { + $this->query .= "= ?\n"; + array_push($this->param, $item); + return $this; + } + + public function ne($item) { + $this->query .= "<> ?\n"; + array_push($this->param, $item); + return $this; + } + + public function where_in($column, $array) { + if (!$this->where) { + $this->where = TRUE; + $this->query .= "WHERE "; + } + if (empty($array)) { + $this->query .= "FALSE\n"; + return $this; + } + $in = $this->in($array); + $this->query .= "$column $in\n"; + return $this; + } + + public function and() { + $this->query .= "AND "; + return $this; + } + + public function or() { + $this->query .= "OR "; + return $this; + } + + public function join($table, $on, $type = 'LEFT') { + $this->query .= "$type JOIN $table ON $on\n"; + return $this; + } + + public function limit($limit) { + $this->query .= "LIMIT ?\n"; + array_push($this->param, $limit); + return $this; + } + + public function offset($offset) { + $this->query .= "OFFSET ?\n"; + array_push($this->param, $offset); + return $this; + } + + public function rows() { + $stmt = $this->conn->prepare($this->query); + try { + $stmt->execute($this->param); + } catch (Exception $ex) { + echo $ex; + echo '<br> >> caused by <<<br>'; + echo str_replace("\n", "<br>", $this->query); + } + return $stmt->fetchAll(PDO::FETCH_ASSOC); + } + + public function row() { + $stmt = $this->conn->prepare($this->query); + $stmt->execute($this->param); + return $stmt->fetch(PDO::FETCH_ASSOC); + } +} + +/** + * DatabaseHelper + * allows queries on the + * xssbook postgres database + */ +class DatabaseHelper { + + private $conn; + + function __construct() { + $this->conn = NULL; + } + + private function connect() { + if ($this->conn === NULL) { + $user = getenv("POSTGRES_USER"); + $pass = getenv("POSTGRES_PASSWORD"); + $db = getenv("POSTGRES_DB"); + $host = 'db'; + $port = '5432'; + + $conn_str = sprintf("pgsql:host=%s;port=%d;dbname=%s;user=%s;password=%s", + $host, + $port, + $db, + $user, + $pass + ); + $this->conn = new \PDO($conn_str); + $this->conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + } + return $this->conn; + } + + public function select($select) { + $conn = $this->connect(); + $query = new DatabaseQuery($conn); + return $query->select($select); + } + +} +?> diff --git a/web/core/error.php b/web/core/error.php new file mode 100644 index 0000000..2e02cb1 --- /dev/null +++ b/web/core/error.php @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<html> + <head> + <title><?=$code . ' - ' . $msg?></title> + </head> + <body> + <center> + <h1><?=$code . ' ' . $msg?></h1> + </center> + <hr> + </body> +</html> diff --git a/web/core/helper.php b/web/core/helper.php new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/web/core/helper.php diff --git a/web/core/loader.php b/web/core/loader.php new file mode 100644 index 0000000..4d4526c --- /dev/null +++ b/web/core/loader.php @@ -0,0 +1,38 @@ +<?php /* Copyright (c) 2024 Freya Murphy */ +class Loader { + + /** + * Loads the given common lang + * @param lang_code - the language code + */ + public function lang($lang_code) { + $dir = $GLOBALS['webroot'] . '/lang/' . $lang_code . '/'; + $lang = $GLOBALS['lang']; + if ($handle = opendir($dir)) { + while (false !== ($entry = readdir($handle))) { + if ($entry === '.' || $entry === '..' || $entry === 'routes') { + continue; + } + $path = $dir . $entry; + require($path); + } + } + $GLOBALS['lang'] = $lang; + } + + /** + * Loads a given route specific lang + * @param lang_coed - the language code + * #param name - the name of the route + */ + public function route_lang($lang_code, $name) { + $dir = $GLOBALS['webroot'] . '/lang/' . $lang_code . '/routes/'; + $file = $dir . $name . '.php'; + if (file_exists($file)) { + $lang = $GLOBALS['lang']; + require($dir . $name . '.php'); + $GLOBALS['lang'] = $lang; + } + } + +} diff --git a/web/core/main.php b/web/core/main.php new file mode 100644 index 0000000..c3c65dd --- /dev/null +++ b/web/core/main.php @@ -0,0 +1,123 @@ +<?php /* Copyright (c) 2024 Freya Murphy */ +class MainModel { + + // loaded route infomation + public $info; + public $db; + public $user_id; + + private $users; + + function __construct() { + $this->info = NULL; + $this->db = new DatabaseHelper(); + $this->users = array(); + $_SESSION['jwt'] = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicmVzdF91c2VyIiwidXNlcl9pZCI6MSwiZXhwIjoxNzExODUxMDUzfQ.FUcFO44SWV--YtVOy7NftTF8OeeOYGZDaDHigygQxsY'; + if (array_key_exists('jwt', $_SESSION)) { + $this->get_session($_SESSION['jwt']); + } else { + $this->user_id = NULL; + }; + } + + private function get_session($jwt) { + $query = $this->db + ->select("_api.verify_jwt('" . $jwt . "') AS user_id;"); + $result = $query->row(); + $user_id = $result['user_id']; + if ($user_id) { + $this->user_id = $user_id; + } + } + + public function link_css($path) { + return '<link rel="stylesheet" href="/public/' . $path . '">'; + } + + public function link_js($path) { + return '<script src="/public/'. $path . '"></script>'; + } + + public function user() { + if ($this->user_id) { + return $this->db + ->select('*') + ->from('api.user') + ->where('id') + ->eq($this->user_id) + ->row(); + } else { + return NULL; + } + } + + public function get_num($key, $default = NULL) { + if (!array_key_exists($key, $_GET)) { + if ($default !== NULL) { + return $default; + } else { + error_page(400, lang('error_400')); + } + } else { + $val = $_GET[$key]; + $val = intval($val); + if ($val < 0) { + return 0; + } else { + return $val; + } + } + } + + public function get_users($objs) { + $ids = array(); + foreach ($objs as $obj) { + $id = $obj['user_id']; + if (!array_key_exists($id, $this->users)) { + array_push($ids, intval($id)); + } + } + if (!empty($ids)) { + $result = $this->db + ->select('*') + ->from('api.user') + ->where_in('id', $ids) + ->rows(); + foreach ($result as $user) { + $id = $user['id']; + $this->users[$id] = $user; + } + } + return $this->users; + } + + public function display_name($user) { + $name = ''; + if ($user['first_name']) { + $name .= $user['first_name']; + } + if ($user['middle_name']) { + if ($name != '') { + $name .= ' '; + } + $name .= $user['middle_name']; + } + if ($user['last_name']) { + if ($name != '') { + $name .= ' '; + } + $name .= $user['last_name']; + } + if ($name == '') { + $name = '@' . $user['username']; + } + return $name; + } + + public function display_date($date) { + return $date; + } + +} + +?> diff --git a/web/core/model.php b/web/core/model.php new file mode 100644 index 0000000..039b138 --- /dev/null +++ b/web/core/model.php @@ -0,0 +1,29 @@ +<?php /* Copyright (c) 2024 Freya Murphy */ +abstract class Model { + // the main model + // shared by all controllers and models + public $main; + public $load; + + // the database + public $db; + + private $config; + + function __construct() { + $this->main = $GLOBALS['__vars']['main']; + $this->load = $GLOBALS['__vars']['load']; + $this->db = $this->main->db; + $this->config = new Aesthetic(); + } + + public function get_data() { + $data = array(); + $route = $this->main->info['route']; + $files = $this->config->get_files($route); + $data = array_merge($data, $files); + $data['self'] = $this->main->user(); + return $data; + } +} +?> diff --git a/web/core/router.php b/web/core/router.php new file mode 100644 index 0000000..6ee28a9 --- /dev/null +++ b/web/core/router.php @@ -0,0 +1,127 @@ +<?php /* Copyright (c) 2024 Freya Murphy */ +class Router { + + private $main; + private $load; + private $routes; + + function load_route($route) { + $name = $route['name']; + $controller_cls = $route['controller']; + $model_cls = $route['model']; + + $root = $GLOBALS['webroot']; + $dir = $root . '/routes/' . $name; + require($dir . '/model.php'); + require($dir . '/controller.php'); + + $model_ref = new ReflectionClass($model_cls); + $model = $model_ref->newInstance(); + + $controller_ref = new ReflectionClass($controller_cls); + $controller = $controller_ref->newInstance($model); + + return $controller; + } + + function __construct($main, $load) { + + $routes = array( + 'home' => array( + 'slugs' => ['', 'home'], + 'model' => 'HomeModel', + 'controller' => 'HomeController', + ), + ); + + $this->routes = array(); + foreach ($routes as $name => $route) { + foreach ($route['slugs'] as $slug) { + $this->routes[$slug] = $route; + $this->routes[$slug]['name'] = $name; + } + } + + $this->main = $main; + $this->load = $load; + } + + function get_info() { + $uri = parse_url($_SERVER['REQUEST_URI']); + $method = $_SERVER['REQUEST_METHOD']; + $parts = explode('/', $uri['path']); + + $slug = sizeof($parts) > 1 ? $parts[1] : ''; + $path = sizeof($parts) > 2 ? $parts[2] : 'index'; + + if (sizeof($parts) > 3) { + return NULL; + } + + return array( + 'method' => $method, + 'uri' => $uri, + + 'slug' => $slug, + 'path' => $path + ); + } + + function handle_error($code) { + $route = array( + 'name' => 'error', + 'model' => 'ErrorModel', + 'controller' => 'ErrorController' + ); + $this->main->info = array( + 'slug' => 'error', + 'lang' => 'en_US', + 'route' => 'error' + ); + $controller = $this->load_route($route); + $_GET['code'] = $code; + http_response_code($code); + $controller->index(); + } + + public function handle_request() { + $request = $this->get_info(); + + if ($request === NULL) { + $this->handle_error(404); + return; + } + + $slug = $request['slug']; + if (!array_key_exists($slug, $this->routes)) { + $this->handle_error(404); + return; + } + + $route = $this->routes[$slug]; + $this->main->info = array( + 'lang' => 'en_US', + 'slug' => $slug, + 'route' => $route['name'], + ); + + $controller = $this->load_route($route); + + $path = $request['path']; + $ref = NULL; + + try { + $ref = new ReflectionMethod($controller, $path); + } catch (Exception $_e) {} + + if ($ref === NULL || !$ref->isPublic()) { + $this->handle_error(404); + return; + + } + + $ref->invoke($controller); + + } + +} |