summaryrefslogtreecommitdiff
path: root/web/core
diff options
context:
space:
mode:
authorFreya Murphy <freya@freyacat.org>2024-03-29 22:29:56 -0400
committerFreya Murphy <freya@freyacat.org>2024-03-29 22:29:56 -0400
commit944b6b0526032ad8c1b4a2612d6723bec75e0e4c (patch)
treed3da5584df33a7878c087622b4fc2ec2883cf880 /web/core
downloadxssbook2-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.php55
-rw-r--r--web/core/controller.php55
-rw-r--r--web/core/database.php173
-rw-r--r--web/core/error.php12
-rw-r--r--web/core/helper.php0
-rw-r--r--web/core/loader.php38
-rw-r--r--web/core/main.php123
-rw-r--r--web/core/model.php29
-rw-r--r--web/core/router.php127
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);
+
+ }
+
+}