From 3a82baec9d793edf81ac2b151b0f4d4159641375 Mon Sep 17 00:00:00 2001 From: Freya Murphy Date: Mon, 1 Apr 2024 11:09:25 -0400 Subject: login and register, liking on homepage --- src/web/_controller/_index.php | 23 +++++ src/web/_controller/_util/post.php | 198 ++++++++++++++++++++++++++++++++++++ src/web/_controller/apps/auth.php | 56 ++++++++++ src/web/_controller/apps/error.php | 21 ++++ src/web/_controller/apps/home.php | 26 +++++ src/web/_controller/modal.php | 34 +++++++ src/web/_controller/template.php | 22 ++++ src/web/_model/apps/auth.php | 13 +++ src/web/_model/apps/error.php | 31 ++++++ src/web/_model/apps/home.php | 22 ++++ src/web/_model/cache.php | 37 +++++++ src/web/_model/format.php | 45 ++++++++ src/web/_model/main.php | 96 +++++++++++++++++ src/web/_model/request.php | 40 ++++++++ src/web/_views/apps/auth/login.php | 86 ++++++++++++++++ src/web/_views/apps/error/main.php | 6 ++ src/web/_views/apps/home/main.php | 27 +++++ src/web/_views/footer.php | 8 ++ src/web/_views/header.php | 62 +++++++++++ src/web/_views/header_empty.php | 23 +++++ src/web/_views/modal/new_post.php | 59 +++++++++++ src/web/_views/modal/register.php | 173 +++++++++++++++++++++++++++++++ src/web/_views/template/comment.php | 15 +++ src/web/_views/template/error.php | 12 +++ src/web/_views/template/modal.php | 14 +++ src/web/_views/template/pfp.php | 8 ++ src/web/_views/template/post.php | 86 ++++++++++++++++ src/web/_views/template/posts.php | 23 +++++ src/web/_views/template/toast.php | 26 +++++ src/web/config/aesthetic.php | 64 ++++++++++++ src/web/config/routes.php | 8 ++ src/web/core/_controller.php | 64 ++++++++++++ src/web/core/_model.php | 44 ++++++++ src/web/core/database.php | 189 ++++++++++++++++++++++++++++++++++ src/web/core/loader.php | 101 ++++++++++++++++++ src/web/core/router.php | 147 ++++++++++++++++++++++++++ src/web/helper/error.php | 9 ++ src/web/helper/lang.php | 77 ++++++++++++++ src/web/index.php | 33 ++++++ src/web/lang/en_US/api_lang.php | 32 ++++++ src/web/lang/en_US/apps/auth.php | 34 +++++++ src/web/lang/en_US/apps/home.php | 9 ++ src/web/lang/en_US/common_lang.php | 50 +++++++++ src/web/lang/en_US/error_lang.php | 8 ++ 44 files changed, 2161 insertions(+) create mode 100644 src/web/_controller/_index.php create mode 100644 src/web/_controller/_util/post.php create mode 100644 src/web/_controller/apps/auth.php create mode 100644 src/web/_controller/apps/error.php create mode 100644 src/web/_controller/apps/home.php create mode 100644 src/web/_controller/modal.php create mode 100644 src/web/_controller/template.php create mode 100644 src/web/_model/apps/auth.php create mode 100644 src/web/_model/apps/error.php create mode 100644 src/web/_model/apps/home.php create mode 100644 src/web/_model/cache.php create mode 100644 src/web/_model/format.php create mode 100644 src/web/_model/main.php create mode 100644 src/web/_model/request.php create mode 100644 src/web/_views/apps/auth/login.php create mode 100644 src/web/_views/apps/error/main.php create mode 100644 src/web/_views/apps/home/main.php create mode 100644 src/web/_views/footer.php create mode 100644 src/web/_views/header.php create mode 100644 src/web/_views/header_empty.php create mode 100644 src/web/_views/modal/new_post.php create mode 100644 src/web/_views/modal/register.php create mode 100644 src/web/_views/template/comment.php create mode 100644 src/web/_views/template/error.php create mode 100644 src/web/_views/template/modal.php create mode 100644 src/web/_views/template/pfp.php create mode 100644 src/web/_views/template/post.php create mode 100644 src/web/_views/template/posts.php create mode 100644 src/web/_views/template/toast.php create mode 100644 src/web/config/aesthetic.php create mode 100644 src/web/config/routes.php create mode 100644 src/web/core/_controller.php create mode 100644 src/web/core/_model.php create mode 100644 src/web/core/database.php create mode 100644 src/web/core/loader.php create mode 100644 src/web/core/router.php create mode 100644 src/web/helper/error.php create mode 100644 src/web/helper/lang.php create mode 100644 src/web/index.php create mode 100644 src/web/lang/en_US/api_lang.php create mode 100644 src/web/lang/en_US/apps/auth.php create mode 100644 src/web/lang/en_US/apps/home.php create mode 100644 src/web/lang/en_US/common_lang.php create mode 100644 src/web/lang/en_US/error_lang.php (limited to 'src/web') diff --git a/src/web/_controller/_index.php b/src/web/_controller/_index.php new file mode 100644 index 0000000..2fd7db2 --- /dev/null +++ b/src/web/_controller/_index.php @@ -0,0 +1,23 @@ +main->session) { + $this->redirect('/home'); + } else { + $this->redirect('/auth/login'); + } + } + +} + +?> diff --git a/src/web/_controller/_util/post.php b/src/web/_controller/_util/post.php new file mode 100644 index 0000000..b48816d --- /dev/null +++ b/src/web/_controller/_util/post.php @@ -0,0 +1,198 @@ +request_model = $this->load->model('request'); + $this->cache_model = $this->load->model('cache'); + $this->page_size = 10; + } + + public function index(): void { + $this->view('template/posts'); + } + + public function post(): void { + $pid = $this->request_model->get_int('id', 0); + + $post = $this->db + ->select('p.*, l.id as like_id') + ->from('api.post p') + ->join('api.like l', 'p.id = l.post_id AND l.user_id') + ->eq($pid) + ->where('p.id') + ->eq($pid) + ->row(); + + if (!$post) { + return; + } + + $users = $this->cache_model->get_users([$post]); + $uid = $post['user_id']; + + if (!array_key_exists($uid, $users)) { + return; + } + + $user = $users[$uid]; + + $data = array( + 'user' => $user, + 'page_size' => $this->page_size, + 'post' => $post + ); + $this->view('template/post', $data); + } + + /** + * @return array + */ + public function posts(): array { + $page = $this->request_model->get_int('page', 0); + $max = $this->request_model->get_int('max'); + $offset = $page * $this->page_size; + + $user = $this->main->user(); + $uid = isset($user) ? $user['id'] : NULL; + + $query = $this->db; + + $query = $this->db + ->select('p.*, l.id as like_id') + ->from('api.post p') + ->join('api.like l', 'p.id = l.post_id AND l.user_id') + ->eq($uid); + + if ($max) { + $query = $query + ->where('p.id')->le($max); + } + + $posts = $query + ->order_by('p.id', 'DESC') + ->limit($this->page_size) + ->offset($offset) + ->rows(); + + $users = $this->cache_model->get_users($posts); + $max = 0; + + foreach ($posts as $post) { + $max = max($max, $post['id']); + $data = array(); + $data['page_size'] = $this->page_size; + $data['user'] = $users[$post['user_id']]; + $data['post'] = $post; + $this->view('template/post', $data); + } + + $pc = $this->db + ->select('COUNT(p.id) as pc') + ->from('api.post p') + ->row()['pc']; + + return array( + 'loaded' => count($posts), + 'total' => $pc, + 'page_size' => $this->page_size, + 'max' => $max, + ); + } + + public function comment(): void { + $cid = $this->request_model->get_int('id', 0); + + $comment = $this->db + ->select('*') + ->from('api.comment') + ->where('id') + ->eq($cid) + ->row(); + + if (!$comment) { + return; + } + + $users = $this->cache_model->get_users([$comment]); + $uid = $comment['user_id']; + + if (!array_key_exists($uid, $users)) { + return; + } + + $user = $users[$uid]; + + $data = array( + 'user' => $user, + 'comment' => $comment + ); + $this->view('template/comment', $data); + } + + /** + * @return array + */ + public function comments(): array { + $page = $this->request_model->get_int('page', 0); + $max = $this->request_model->get_int('max'); + $id = $this->request_model->get_int('id', 0); + $offset = $page * $this->page_size; + + $query = $this->db + ->select('*') + ->from('api.comment') + ->where('post_id') + ->eq($id); + + if ($max) { + $query = $query + ->and() + ->where('id') + ->le($max); + } + + $comments = $query + ->order_by('id', 'ASC') + ->limit($this->page_size) + ->offset($offset) + ->rows(); + + $users = $this->cache_model->get_users($comments); + $max = 0; + + // only add this hr when not logged in + // otherwise its added automatically by + // the like and comment buttons + if ( + count($comments) && + $page == 0 && + $this->main->session === NULL + ) { + echo '
'; + } + + foreach ($comments as $comment) { + $max = max($max, $comment['id']); + $data = array(); + $data['user'] = $users[$comment['user_id']]; + $data['comment'] = $comment; + $this->view('template/comment', $data); + } + + return array( + 'loaded' => count($comments), + 'page_size' => $this->page_size, + 'max' => $max, + ); + } +} diff --git a/src/web/_controller/apps/auth.php b/src/web/_controller/apps/auth.php new file mode 100644 index 0000000..6b30cc9 --- /dev/null +++ b/src/web/_controller/apps/auth.php @@ -0,0 +1,56 @@ +auth_model = $this->load->model('apps/auth'); + } + + public function index(): void { + if ($this->main->session) { + $this->redirect('/home'); + } else { + $this->redirect('/auth/login'); + } + } + + public function login(): void { + if ($this->main->session) { + $this->redirect('/home'); + } + + parent::index(); + $data = $this->auth_model->get_data(); + $this->view('header_empty', $data); + $this->view('apps/auth/login', $data); + $this->view('footer', $data); + } + + public function logout(): void { + if ($this->main->session) { + $_SESSION['jwt'] = NULL; + } + $this->redirect('/auth/login'); + } + + public function update(): void { + if (!$this->is_ajax()) { + $this->error(400); + } + if (!isset($_POST['key']) || !isset($_POST['value'])) { + $this->error(400); + } + $key = $_POST['key']; + $value = $_POST['value']; + $_SESSION[$key] = $value; + } + +} + +?> diff --git a/src/web/_controller/apps/error.php b/src/web/_controller/apps/error.php new file mode 100644 index 0000000..03bbd8d --- /dev/null +++ b/src/web/_controller/apps/error.php @@ -0,0 +1,21 @@ +error_model = $this->load->model('apps/error'); + } + + public function index(): void { + parent::index(); + $data = $this->error_model->get_data(); + $this->view('header', $data); + $this->view('apps/error/main', $data); + $this->view('footer', $data); + } + +} + +?> diff --git a/src/web/_controller/apps/home.php b/src/web/_controller/apps/home.php new file mode 100644 index 0000000..c9a116d --- /dev/null +++ b/src/web/_controller/apps/home.php @@ -0,0 +1,26 @@ +home_model = $this->load->model('apps/home'); + $this->post_controller = $this->load->controller('_util/post'); + } + + public function index(): void { + parent::index(); + $data = $this->home_model->get_data(); + $this->view('header', $data); + $this->view('apps/home/main', $data); + $this->view('footer', $data); + } + +} + +?> diff --git a/src/web/_controller/modal.php b/src/web/_controller/modal.php new file mode 100644 index 0000000..03074d4 --- /dev/null +++ b/src/web/_controller/modal.php @@ -0,0 +1,34 @@ +view('template/modal', $data); + } + + public function new_post(): void { + $this->modal('new_post'); + } + + public function register(): void { + $this->load->app_lang( + $this->main->info['lang'], + 'auth' + ); + $this->modal('register'); + } +} + +?> + diff --git a/src/web/_controller/template.php b/src/web/_controller/template.php new file mode 100644 index 0000000..7a8cdf8 --- /dev/null +++ b/src/web/_controller/template.php @@ -0,0 +1,22 @@ +request_model = $this->load->model('request'); + } + + public function toast(): void { + $data = array( + 'msg' => $this->request_model->get_str('msg', FALSE), + 'detail' => $this->request_model->get_str('detail', FALSE), + 'hint' => $this->request_model->get_str('hint', FALSE) + ); + $this->view('template/toast', $data); + } + +} + diff --git a/src/web/_model/apps/auth.php b/src/web/_model/apps/auth.php new file mode 100644 index 0000000..a1802de --- /dev/null +++ b/src/web/_model/apps/auth.php @@ -0,0 +1,13 @@ +get_msg($data); + return $data; + } +} +?> diff --git a/src/web/_model/apps/home.php b/src/web/_model/apps/home.php new file mode 100644 index 0000000..82fbf26 --- /dev/null +++ b/src/web/_model/apps/home.php @@ -0,0 +1,22 @@ +db + ->select('*') + ->from('admin.post') + ->limit(20) + ->rows(); + } + + public function get_data(): array { + $data = parent::get_data(); + $data['title'] = lang('title'); + $data['posts'] = $this->get_posts(); + return $data; + } +} diff --git a/src/web/_model/cache.php b/src/web/_model/cache.php new file mode 100644 index 0000000..6cf9924 --- /dev/null +++ b/src/web/_model/cache.php @@ -0,0 +1,37 @@ +users = array(); + } + + /** + * Gets a array of users + */ + 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->main->db + ->select('*') + ->from('api.user') + ->where_in('id', $ids) + ->rows(); + foreach ($result as $user) { + $id = $user['id']; + $this->users[$id] = $user; + } + } + return $this->users; + } + +} diff --git a/src/web/_model/format.php b/src/web/_model/format.php new file mode 100644 index 0000000..52b51be --- /dev/null +++ b/src/web/_model/format.php @@ -0,0 +1,45 @@ +db = new DatabaseHelper(); + /// load the current session + if (array_key_exists('jwt', $_SESSION)) { + $this->get_session($_SESSION['jwt']); + } else { + $this->session = NULL; + }; + /// init other vars + $this->users = array(); + } + + /** + * Loads current session + * @param string $jwt - the user provided JWT + */ + 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->session = array( + 'id' => $user_id, + 'jwt' => $jwt + ); + } + } + + /** + * Gets the stamp for a asset path + * @param string $path + */ + private function asset_stamp($path): int { + $root = $GLOBALS['webroot']; + $path = $root . '/../public/' . $path; + return filemtime($path); + } + + /** + * Loads a css html link + * @param string $path - the path to the css file + */ + public function link_css($path) { + $stamp = $this->asset_stamp($path); + return ''; + } + + /** + * Loads a js html link + * @param string $path - the path to the js file + */ + public function link_js($path) { + $stamp = $this->asset_stamp($path); + return ''; + } + + /** + * Gets the current user + */ + public function user() { + if ($this->session) { + return $this->db + ->select('*') + ->from('api.user') + ->where('id') + ->eq($this->session['id']) + ->row(); + } else { + return NULL; + } + } + +} + +?> diff --git a/src/web/_model/request.php b/src/web/_model/request.php new file mode 100644 index 0000000..4cce07a --- /dev/null +++ b/src/web/_model/request.php @@ -0,0 +1,40 @@ + + +
+
+

xssbook

+ +
+
+
+
+ + +
+
+ + +
+ 'submit') + )?> + +
+
+ 'submit') + )?> +
+ +
diff --git a/src/web/_views/apps/error/main.php b/src/web/_views/apps/error/main.php new file mode 100644 index 0000000..dde39cf --- /dev/null +++ b/src/web/_views/apps/error/main.php @@ -0,0 +1,6 @@ + + +
+

+ +
diff --git a/src/web/_views/apps/home/main.php b/src/web/_views/apps/home/main.php new file mode 100644 index 0000000..29bf7c3 --- /dev/null +++ b/src/web/_views/apps/home/main.php @@ -0,0 +1,27 @@ + + +
+ +
+
+ view('template/pfp', array('user' => $self))?> + + + +
+ +
+ + post_controller->index(); ?> +
diff --git a/src/web/_views/footer.php b/src/web/_views/footer.php new file mode 100644 index 0000000..9040c3a --- /dev/null +++ b/src/web/_views/footer.php @@ -0,0 +1,8 @@ + + + + + + diff --git a/src/web/_views/header.php b/src/web/_views/header.php new file mode 100644 index 0000000..7c60197 --- /dev/null +++ b/src/web/_views/header.php @@ -0,0 +1,62 @@ + + +main->user(); + $this->view('header_empty', $data); +?> + diff --git a/src/web/_views/header_empty.php b/src/web/_views/header_empty.php new file mode 100644 index 0000000..75f6f17 --- /dev/null +++ b/src/web/_views/header_empty.php @@ -0,0 +1,23 @@ + + + + + main->link_js($js); + } + foreach ($css_files as $css) { + echo $this->main->link_css($css); + } + ?> + <?=$title?> + + +
+
diff --git a/src/web/_views/modal/new_post.php b/src/web/_views/modal/new_post.php new file mode 100644 index 0000000..50b9b84 --- /dev/null +++ b/src/web/_views/modal/new_post.php @@ -0,0 +1,59 @@ + + +main->user(); +?> +
+
+
+ view('template/pfp', array('user' => $user))?> +
+ + +
+
+ +
+ +
+ diff --git a/src/web/_views/modal/register.php b/src/web/_views/modal/register.php new file mode 100644 index 0000000..f4d364a --- /dev/null +++ b/src/web/_views/modal/register.php @@ -0,0 +1,173 @@ + + + +
+ + +
+ diff --git a/src/web/_views/template/comment.php b/src/web/_views/template/comment.php new file mode 100644 index 0000000..3ff473b --- /dev/null +++ b/src/web/_views/template/comment.php @@ -0,0 +1,15 @@ + + +load->model('format'); +?> +
+ view('template/pfp', array('user' => $user))?> +
+
+ name($user)?> + date($comment['created'])?> +
+ +
+
diff --git a/src/web/_views/template/error.php b/src/web/_views/template/error.php new file mode 100644 index 0000000..2e02cb1 --- /dev/null +++ b/src/web/_views/template/error.php @@ -0,0 +1,12 @@ + + + + <?=$code . ' - ' . $msg?> + + +
+

+
+
+ + diff --git a/src/web/_views/template/modal.php b/src/web/_views/template/modal.php new file mode 100644 index 0000000..e3ce6fe --- /dev/null +++ b/src/web/_views/template/modal.php @@ -0,0 +1,14 @@ + + + diff --git a/src/web/_views/template/pfp.php b/src/web/_views/template/pfp.php new file mode 100644 index 0000000..aec7318 --- /dev/null +++ b/src/web/_views/template/pfp.php @@ -0,0 +1,8 @@ + + + + + + diff --git a/src/web/_views/template/post.php b/src/web/_views/template/post.php new file mode 100644 index 0000000..83a72bf --- /dev/null +++ b/src/web/_views/template/post.php @@ -0,0 +1,86 @@ + + +
+
+ view('template/pfp', array('user' => $user))?> +
+ + +
+
+

+ +

+main->user(); + $liked = $post['like_id'] ? 'btn-blue' : ''; + $post_attrs = array( + 'postId' => $post['id'] + ); + if ($post['like_id'] !== NULL) { + $post_attrs['likeId'] = $post['like_id']; + } +?> + +
+
+ + +
+
+ +
+ $post['id']); + $cdata = $this->comments(); + + $loaded = $cdata['loaded']; + $max = $cdata['max']; + $page_size = $cdata['page_size']; + $total = $post['comment_count']; + + if ($loaded >= $page_size && $page_size < $total) { + ilang('action_load_comments', + class: 'action-load-comments btn btn-line mt', + attrs: array( + 'postId' => $post['id'], + 'loaded' => $loaded, + 'pageSize' => $page_size, + 'commentCount' => $total, + 'commentMax' => $max, + ) + ); + } + + ?> +
+ +
+ view('template/pfp', array('user' => $user))?> +
+ + +
+
+ +
+ + diff --git a/src/web/_views/template/posts.php b/src/web/_views/template/posts.php new file mode 100644 index 0000000..5e9156c --- /dev/null +++ b/src/web/_views/template/posts.php @@ -0,0 +1,23 @@ +
+posts(); + + $loaded = $pdata['loaded']; + $page_size = $pdata['page_size']; + $total = $pdata['total']; + $max = $pdata['max']; + + if ($loaded >= $page_size && $page_size < $total) { + ilang('action_load_posts', + id: 'action-load-posts', + class: 'btn btn-line btn-wide mb', + attrs: array( + 'loaded' => $loaded, + 'pageSize' => $page_size, + 'postCount' => $total, + 'postMax' => $max, + ) + ); + } +?> +
diff --git a/src/web/_views/template/toast.php b/src/web/_views/template/toast.php new file mode 100644 index 0000000..ae2e7d8 --- /dev/null +++ b/src/web/_views/template/toast.php @@ -0,0 +1,26 @@ + + + +
+ + +
diff --git a/src/web/config/aesthetic.php b/src/web/config/aesthetic.php new file mode 100644 index 0000000..304baec --- /dev/null +++ b/src/web/config/aesthetic.php @@ -0,0 +1,64 @@ +config = array( + '_common' => array( + 'js' => [ + 'js/thirdparty/jquery.min.js', + 'js/lib.js', + 'js/modal.js', + ], + 'css' => [ + 'css/common.css' + ], + ), + 'error' => array( + 'css' => [ + 'css/error.css' + ], + ), + 'home' => array( + 'js' => [ + 'js/routes/home.js', + 'js/post.js', + ], + 'css' => [ + 'css/home.css', + 'css/post.css' + ], + ), + 'auth' => array( + 'css' => [ + 'css/auth.css' + ], + ), + ); + } + /** + * @param mixed $route + * @return array + */ + function get_files($route): array { + $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/src/web/config/routes.php b/src/web/config/routes.php new file mode 100644 index 0000000..33c871b --- /dev/null +++ b/src/web/config/routes.php @@ -0,0 +1,8 @@ +load = $load; + $this->main = $this->load->model('main'); + $this->db = $this->main->db; + + $info = $this->main->info; + $lang = $info['lang']; + $this->load->lang($lang); + $app = $info['app']; + if ($app) { + $this->load->app_lang($lang, $app); + } + } + + public function index() {} + + public function redirect($link) { + header('Location: '. $link, true, 301); + die(); + } + + 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 is_ajax(): bool { + $_POST = json_decode( + file_get_contents("php://input"), true + ); + return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest'; + } + + protected function error($code): void { + $_GET['code'] = $code; + $this->main->info['app'] = 'error'; + $error_controller = $this->load->controller('apps/error'); + $error_controller->index(); + die(); + } + +} +?> diff --git a/src/web/core/_model.php b/src/web/core/_model.php new file mode 100644 index 0000000..936fab4 --- /dev/null +++ b/src/web/core/_model.php @@ -0,0 +1,44 @@ +load = $load; + $this->main = $this->load->model('main'); + $this->db = $this->main->db; + $this->config = new Aesthetic(); + } + + /** + * @returns the base model data + */ + public function get_data(): array { + $data = array(); + $data['self'] = $this->main->user(); + + $info = $this->main->info; + $app = $info['app']; + + if ($app) { + $files = $this->config->get_files($app); + $data = array_merge($data, $files); + } else { + $files = $this->config->get_files(); + $data = array_merge($data, $files); + } + + return $data; + } +} diff --git a/src/web/core/database.php b/src/web/core/database.php new file mode 100644 index 0000000..81352a9 --- /dev/null +++ b/src/web/core/database.php @@ -0,0 +1,189 @@ +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 lt($item) { + $this->query .= "< ?\n"; + array_push($this->param, $item); + return $this; + } + + public function le($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 order_by($column, $order = 'ASC') { + $this->query .= "ORDER BY " . $column . ' ' . $order . ' '; + return $this; + } + + public function rows() { + $stmt = $this->conn->prepare($this->query); + try { + $stmt->execute($this->param); + } catch (Exception $ex) { + echo $ex; + echo '
>> caused by <<
'; + echo str_replace("\n", "
", $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/src/web/core/loader.php b/src/web/core/loader.php new file mode 100644 index 0000000..2091533 --- /dev/null +++ b/src/web/core/loader.php @@ -0,0 +1,101 @@ +loaded = array(); + } + + /** + * Loads a $type of object from a $dir with a given $name + * @param string $name - the name of the object to load + * @param string $dir - the directory theese objects are stored in + * @param string $type - the type of the object + */ + private function load_type($name, $dir, $type): object|NULL { + $path = $dir . '/' . $name . '.php'; + if (array_key_exists($path, $this->loaded)) { + return $this->loaded[$path]; + } + + if (!file_exists($path)) { + return NULL; + } + + $parts = explode('/', $name); + $part = end($parts); + $class = ucfirst($part) . '_' . $type; + require($path); + + $ref = NULL; + try { + $ref = new ReflectionClass($class); + } catch (Exception $_e) {} + + if ($ref === NULL) { + return NULL; + } + + $obj = $ref->newInstance($this); + $this->loaded[$path] = $obj; + + return $obj; + } + + /** + * Loads a model + * @param string $name - the name of the model to load + */ + public function model($name): object|NULL { + $root = $GLOBALS['webroot']; + $dir = $root . '/_model'; + return $this->load_type($name, $dir, 'model'); + } + + /** + * Loads a controller + * @param string $name - the name of the controller to load + */ + public function controller($name): Controller|NULL { + $root = $GLOBALS['webroot']; + $dir = $root . '/_controller'; + return $this->load_type($name, $dir, 'controller'); + } + + /** + * Loads the given common lang + * @param string $lang_code 0 the language code + */ + public function lang($lang_code): void { + $dir = $GLOBALS['webroot'] . '/lang/' . $lang_code . '/'; + $lang = $GLOBALS['lang']; + if ($handle = opendir($dir)) { + while (false !== ($entry = readdir($handle))) { + if ($entry === '.' || $entry === '..' || $entry === 'apps') { + continue; + } + $path = $dir . $entry; + require($path); + } + } + $GLOBALS['lang'] = $lang; + } + + /** + * Loads a given app specific lang + * @param string $lang_code - the language code + * @param string $name - the name of the app + */ + public function app_lang($lang_code, $name): void { + $dir = $GLOBALS['webroot'] . '/lang/' . $lang_code . '/apps/'; + $file = $dir . $name . '.php'; + if (file_exists($file)) { + $lang = $GLOBALS['lang']; + require($dir . $name . '.php'); + $GLOBALS['lang'] = $lang; + } + } + +} diff --git a/src/web/core/router.php b/src/web/core/router.php new file mode 100644 index 0000000..72c7674 --- /dev/null +++ b/src/web/core/router.php @@ -0,0 +1,147 @@ +load = $load; + $this->main = $this->load->model('main'); + } + + /** + * @param string $path - the current request path + * Gets the current route + * @return array + */ + 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( + 'route' => '', + 'slug' => 'index', + ); + // e.g. /home /login + } else if ($len === 1) { + $route = array( + 'route' => $parts[0], + 'slug' => 'index', + ); + // e.g. /home/posts + } else { + $route = array ( + 'route' => implode('/', array_slice($parts, 0, -1)), + 'slug' => end($parts) + ); + }; + + $route['app'] = $route['route']; + $routes = $GLOBALS['routes']; + if (array_key_exists($route['route'], $routes)) { + $route['route'] = $routes[$route['route']]; + } + + return $route; + } + + /** + * Gets the curret request info + * @return array + */ + private function get_req(): array { + $method = $_SERVER['REQUEST_METHOD']; + + $uri = parse_url($_SERVER['REQUEST_URI']); + $path = $uri['path']; + + return array_merge( + array( + 'uri' => $uri, + 'method' => $method, + 'lang' => $this->get_lang(), + ), + $this->get_req_route($path), + ); + } + + /** + * Gets the current language + * @return string + */ + private function get_lang(): string { + return 'en_US'; + } + + /** + * 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)'); + } + + $this->main->info['slug'] = 'index'; + $this->main->info['app'] = 'error'; + $this->main->info['route'] = 'apps/error'; + $req = $this->main->info; + $_GET['code'] = $code; + + $this->handle_req($req, TRUE); + } + + /** + * @param array $req + * @param bool $recursed + */ + private function handle_req($req, $recursed = FALSE): void { + $controller = $this->load->controller($req['route']); + + 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); + } + + /** + * Handels the incomming reuqest + */ + public function handle_request(): void { + $req = $this->get_req(); + $this->main->info = $req; + $this->handle_req($req); + } + +} diff --git a/src/web/helper/error.php b/src/web/helper/error.php new file mode 100644 index 0000000..6fcaddd --- /dev/null +++ b/src/web/helper/error.php @@ -0,0 +1,9 @@ + $attr) { + echo $key . '="' . $attr . '" '; + } + echo '> '; + if ($icon) { + echo ''; + if ($content) { + echo $content; + } + echo ''; + } + if ($text) { + echo '' . $text . ''; + } + if ($click || $button) { + echo ''; + } else { + echo ''; + } +} diff --git a/src/web/index.php b/src/web/index.php new file mode 100644 index 0000000..688383f --- /dev/null +++ b/src/web/index.php @@ -0,0 +1,33 @@ +handle_request(); +}; + +if (!file_exists('/status/ready')) { + error_page(503, 'Service Unavailable'); +} + +__init(); diff --git a/src/web/lang/en_US/api_lang.php b/src/web/lang/en_US/api_lang.php new file mode 100644 index 0000000..3afc4f6 --- /dev/null +++ b/src/web/lang/en_US/api_lang.php @@ -0,0 +1,32 @@ + diff --git a/src/web/lang/en_US/apps/auth.php b/src/web/lang/en_US/apps/auth.php new file mode 100644 index 0000000..fb9d758 --- /dev/null +++ b/src/web/lang/en_US/apps/auth.php @@ -0,0 +1,34 @@ + diff --git a/src/web/lang/en_US/apps/home.php b/src/web/lang/en_US/apps/home.php new file mode 100644 index 0000000..a30eb88 --- /dev/null +++ b/src/web/lang/en_US/apps/home.php @@ -0,0 +1,9 @@ + diff --git a/src/web/lang/en_US/common_lang.php b/src/web/lang/en_US/common_lang.php new file mode 100644 index 0000000..7e214b5 --- /dev/null +++ b/src/web/lang/en_US/common_lang.php @@ -0,0 +1,50 @@ + diff --git a/src/web/lang/en_US/error_lang.php b/src/web/lang/en_US/error_lang.php new file mode 100644 index 0000000..afecaa1 --- /dev/null +++ b/src/web/lang/en_US/error_lang.php @@ -0,0 +1,8 @@ + -- cgit v1.2.3-freya