everything(everything): simply saving work
This commit is contained in:
parent
0f2761cd61
commit
76ca7b9c32
25 changed files with 1330 additions and 561 deletions
17
www/src/db_handler/analytics_interface.php
Normal file
17
www/src/db_handler/analytics_interface.php
Normal file
|
@ -0,0 +1,17 @@
|
|||
|
||||
<?php
|
||||
|
||||
interface AnalyticsInterface {
|
||||
public function log_path_access($path,
|
||||
$agent,
|
||||
$time,
|
||||
$referrer);
|
||||
|
||||
public function log_path_errcode(
|
||||
$path,
|
||||
$agent,
|
||||
$referrer,
|
||||
$code);
|
||||
}
|
||||
|
||||
?>
|
86
www/src/db_handler/db_interface.php
Normal file
86
www/src/db_handler/db_interface.php
Normal file
|
@ -0,0 +1,86 @@
|
|||
|
||||
<?php
|
||||
|
||||
function sanitize_post_path($post_path) {
|
||||
$post_path = chop($post_path, '/');
|
||||
|
||||
if($post_path == "") {
|
||||
return "";
|
||||
}
|
||||
|
||||
if(!preg_match('/^(?:\/[\w-]+)+(?:\.[\w-]+)*$/', $post_path)) {
|
||||
echo "Post path match against " . $post_path . " failed!";
|
||||
die();
|
||||
}
|
||||
|
||||
return $post_path;
|
||||
}
|
||||
|
||||
function escape_tag($tag) {
|
||||
return preg_replace_callback('/[\WZ]/', function($match) {
|
||||
return "Z" . ord($match[0]);
|
||||
}, strtolower($tag));
|
||||
}
|
||||
|
||||
function escape_search_tag($tag) {
|
||||
preg_match("/^([\+\-]?)(.*?)(\*?)$/", $tag, $matches);
|
||||
|
||||
if(!isset($matches[1])) {
|
||||
echo "Problem with tag!";
|
||||
var_dump($tag);
|
||||
}
|
||||
|
||||
return $matches[1] . $this->escape_tag($matches[2]) . $matches[3];
|
||||
}
|
||||
|
||||
interface PostdataInterface {
|
||||
/* Postdata format:
|
||||
*
|
||||
* The Postdata array is a simple intermediate data format
|
||||
* for the Post content and metadata.
|
||||
* It is slightly abstracted from the SQL format itself but will
|
||||
* only reformat keys, *not* do any alteration of the data itself.
|
||||
*
|
||||
* Any supported fields will be integrated into the database.
|
||||
* Other fields will be saved in a JSON structure, and will
|
||||
* be restored afterward.
|
||||
*
|
||||
* The following fields are mandatory for *writing*
|
||||
* - path: String, must be sanitized to consist of just alphanumeric
|
||||
* characters, `_-./`
|
||||
* used to identify the post itself
|
||||
*
|
||||
* The following fields may be returned by the database:
|
||||
* - id
|
||||
* - created_at
|
||||
* - updated_at
|
||||
* - view_count
|
||||
*
|
||||
* The following fields may be supported by the database:
|
||||
* - markdown: String, markdown of the post. May be
|
||||
* stored separately and won't be returned by default!
|
||||
* - type: String, defining the type of the post
|
||||
* - title: String, self-explanatory
|
||||
* - tags: Array of strings
|
||||
* - settings: Hash, recursively merged settings (calculated by DB!)
|
||||
*
|
||||
* The following fields are *recommended*, but nothing more:
|
||||
* - icon: String, optionally defining
|
||||
*/
|
||||
|
||||
public function stub_postdata($path);
|
||||
public function stub_postdata_tree($path);
|
||||
|
||||
public function set_postdata($data);
|
||||
public function set_post_markdown($id, $markdown);
|
||||
|
||||
public function get_postdata($path);
|
||||
// Returns a key-value pair of child paths => child data
|
||||
public function get_post_children($path,
|
||||
$limit = 50, $depth_start = 1, $depth_end = 1,
|
||||
$order_by = 'path');
|
||||
|
||||
public function get_post_markdown($id);
|
||||
}
|
||||
|
||||
?>
|
348
www/src/db_handler/mysql_handler.php
Normal file
348
www/src/db_handler/mysql_handler.php
Normal file
|
@ -0,0 +1,348 @@
|
|||
|
||||
<?php
|
||||
|
||||
require_once 'analytics_interface.php';
|
||||
require_once 'db_interface.php';
|
||||
|
||||
function taglist_escape_tag($tag) {
|
||||
return preg_replace_callback('/[\WZ]/', function($match) {
|
||||
return "Z" . ord($match[0]);
|
||||
}, strtolower($tag));
|
||||
}
|
||||
|
||||
function taglist_to_sql_string($post_tags) {
|
||||
$post_tags = array_unique($post_tags);
|
||||
$post_tags = array_map(function($val) {
|
||||
return taglist_escape_tag($val);
|
||||
}, $post_tags);
|
||||
|
||||
asort($post_tags);
|
||||
$post_tags = join(' ', $post_tags);
|
||||
|
||||
return $post_tags;
|
||||
}
|
||||
|
||||
class MySQLHandler
|
||||
implements PostdataInterface {
|
||||
|
||||
CONST SQL_READ_COLUMNS = [
|
||||
'id', 'path', 'created_at', 'updated_at',
|
||||
'title', 'view_count', 'brief'];
|
||||
|
||||
CONST SQL_WRITE_COLUMNS = ['path', 'title', 'brief'];
|
||||
|
||||
private $sql_connection;
|
||||
|
||||
public $hostname;
|
||||
|
||||
public $debugging;
|
||||
|
||||
function __construct($sql_connection, $hostname) {
|
||||
$this->sql_connection = $sql_connection;
|
||||
$this->hostname = $hostname;
|
||||
|
||||
$this->debugging = false;
|
||||
}
|
||||
|
||||
private function _dbg($message) {
|
||||
if($this->debugging) {
|
||||
echo $message;
|
||||
}
|
||||
}
|
||||
|
||||
private function _exec($qery, $argtypes = '', ...$args) {
|
||||
$stmt = $this->sql_connection->prepare($qery);
|
||||
|
||||
if($argtypes != ""){
|
||||
$stmt->bind_param($argtypes, ...$args);
|
||||
}
|
||||
$stmt->execute();
|
||||
|
||||
return $stmt->get_result();
|
||||
}
|
||||
|
||||
private function clear_post_settings_cache($post_path) {
|
||||
$post_path = sanitize_post_path($post_path);
|
||||
|
||||
$this->_exec("
|
||||
UPDATE posts
|
||||
SET post_settings_cache=NULL
|
||||
WHERE host = ? AND post_path LIKE ?;
|
||||
", "ss", $this->hostname, $post_path . "%");
|
||||
}
|
||||
|
||||
public function stub_postdata($path) {
|
||||
$post_path = sanitize_post_path($path);
|
||||
$path_depth = substr_count($post_path, "/");
|
||||
|
||||
|
||||
$qry = "
|
||||
INSERT INTO posts
|
||||
(host, post_path, post_path_depth)
|
||||
VALUES
|
||||
( ?, ?, ?) AS new
|
||||
ON DUPLICATE KEY UPDATE post_path=new.post_path;";
|
||||
|
||||
$this->_exec($qry, "ssi",
|
||||
$this->hostname,
|
||||
$post_path,
|
||||
$path_depth);
|
||||
}
|
||||
|
||||
public function stub_postdata_tree($path) {
|
||||
$post_path = sanitize_post_path($path);
|
||||
|
||||
while(true) {
|
||||
if($post_path == '/') {
|
||||
$post_path = '';
|
||||
}
|
||||
|
||||
try {
|
||||
$this->stub_postdata($post_path);
|
||||
}
|
||||
catch(Exception $e) {
|
||||
}
|
||||
|
||||
$post_path = dirname($post_path);
|
||||
if(strlen($post_path) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function set_postdata($data) {
|
||||
$data['path'] = sanitize_post_path($data['path']);
|
||||
$post_path = $data['path'];
|
||||
unset($data['path']);
|
||||
|
||||
$this->stub_postdata_tree($post_path);
|
||||
|
||||
$data['title'] ??= basename($post_path);
|
||||
|
||||
$post_tags = $data['tags'] ?? [];
|
||||
array_push($post_tags,
|
||||
'path:' . $post_path
|
||||
);
|
||||
|
||||
$sql_args = [
|
||||
$this->hostname,
|
||||
$post_path,
|
||||
substr_count($post_path, "/"),
|
||||
$data['title'],
|
||||
taglist_to_sql_string($post_tags),
|
||||
$data['brief'] ?? null
|
||||
];
|
||||
|
||||
unset($data['title']);
|
||||
unset($data['brief']);
|
||||
|
||||
$post_markdown = $data['markdown'] ?? null;
|
||||
unset($data['markdown']);
|
||||
unset($data['html']);
|
||||
|
||||
array_push($sql_args, json_encode($data));
|
||||
|
||||
$qry =
|
||||
"INSERT INTO posts
|
||||
(host,
|
||||
post_path, post_path_depth,
|
||||
post_title, post_tags, post_brief,
|
||||
post_metadata, post_settings_cache)
|
||||
VALUES
|
||||
( ?, ?, ?, ?, ?, ?, ?, null) AS new
|
||||
ON DUPLICATE KEY
|
||||
UPDATE post_title=new.post_title,
|
||||
post_tags=new.post_tags,
|
||||
post_brief=new.post_brief,
|
||||
post_metadata=new.post_metadata,
|
||||
post_updated_at=CURRENT_TIMESTAMP;
|
||||
";
|
||||
|
||||
$this->_exec($qry, "ssissss", ...$sql_args);
|
||||
|
||||
if(isset($post_markdown)) {
|
||||
$this->set_post_markdown($this->sql_connection->insert_id, $post_markdown);
|
||||
}
|
||||
|
||||
$this->clear_post_settings_cache($post_path);
|
||||
}
|
||||
|
||||
public function set_post_markdown($id, $markdown) {
|
||||
$qry =
|
||||
"INSERT INTO post_markdown ( post_id, post_markdown )
|
||||
VALUES (?, ?) AS new
|
||||
ON DUPLICATE KEY UPDATE post_markdown=new.post_markdown;
|
||||
";
|
||||
|
||||
$this->_exec($qry, "is", $id, $markdown);
|
||||
}
|
||||
|
||||
private function get_post_settings($post_path) {
|
||||
$post_path = sanitize_post_path($post_path);
|
||||
|
||||
$this->_dbg("-> gps: getting path " . $post_path . "\n");
|
||||
|
||||
$post_settings = $this->_exec("
|
||||
SELECT post_settings_cache
|
||||
FROM posts
|
||||
WHERE post_path = ? AND host = ?
|
||||
", "ss", $post_path, $this->hostname)->fetch_assoc();
|
||||
|
||||
if(!isset($post_settings)) {
|
||||
$this->_dbg("-> gps: Returning because of no result\n");
|
||||
return [];
|
||||
}
|
||||
if(isset($post_settings['post_settings_cache'])) {
|
||||
$result = json_decode($post_settings['post_settings_cache'], true);
|
||||
if($this->debugging) {
|
||||
echo "-> gps: Returning because of cached result:\n";
|
||||
echo "--> " . json_encode($result) . "\n";
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
$parent_settings = [];
|
||||
if($post_path != "") {
|
||||
$parent_settings = $this->get_post_settings(dirname($post_path));
|
||||
}
|
||||
|
||||
$post_settings = [];
|
||||
$post_metadata = $this->_exec("
|
||||
SELECT post_path, post_metadata
|
||||
FROM posts
|
||||
WHERE post_path = ? AND host = ?
|
||||
", "ss", $post_path, $this->hostname)->fetch_assoc();
|
||||
|
||||
if(isset($post_metadata['post_metadata'])) {
|
||||
$post_metadata = json_decode($post_metadata['post_metadata'], true);
|
||||
|
||||
if(isset($post_metadata['settings'])) {
|
||||
$post_settings = $post_metadata['settings'];
|
||||
}
|
||||
}
|
||||
|
||||
$post_settings = array_merge($parent_settings, $post_settings);
|
||||
|
||||
$this->_dbg("-> gps: Merged post settings are " . json_encode($post_settings) . ", saving...\n");
|
||||
|
||||
$this->_exec("
|
||||
UPDATE posts SET post_settings_cache=? WHERE post_path=? AND host=?
|
||||
", "sss",
|
||||
json_encode($post_settings), $post_path, $this->hostname);
|
||||
|
||||
return $post_settings;
|
||||
}
|
||||
|
||||
private function process_postdata($data) {
|
||||
if(!isset($data)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if(!isset($data['post_path'])) {
|
||||
echo "ERROR, trying to get a post data package without path!";
|
||||
die();
|
||||
}
|
||||
|
||||
$outdata = [];
|
||||
foreach($this::SQL_READ_COLUMNS as $key) {
|
||||
if(isset($data['post_' . $key])) {
|
||||
$outdata[$key] = $data['post_' . $key];
|
||||
}
|
||||
}
|
||||
|
||||
$post_metadata = json_decode($data['post_metadata'] ?? '{}', true);
|
||||
|
||||
$post_settings = [];
|
||||
|
||||
if(isset($data['post_settings_cache'])) {
|
||||
$post_settings = json_decode($data['post_settings_cache'], true);
|
||||
}
|
||||
else {
|
||||
$post_settings = $this->get_post_settings($data['post_path']);
|
||||
}
|
||||
|
||||
$outdata = array_merge($post_settings, $post_metadata, $outdata);
|
||||
|
||||
return $outdata;
|
||||
}
|
||||
|
||||
public function get_postdata($path) {
|
||||
$path = sanitize_post_path($path);
|
||||
|
||||
$qry = "
|
||||
SELECT *
|
||||
FROM posts
|
||||
WHERE post_path = ? AND host = ?;
|
||||
";
|
||||
|
||||
$data = $this->_exec($qry, "ss", $path, $this->hostname)->fetch_assoc();
|
||||
|
||||
return $this->process_postdata($data);
|
||||
}
|
||||
|
||||
public function get_post_children($path,
|
||||
$limit = 50, $depth_start = 1, $depth_end = 1,
|
||||
$order_by = 'path') {
|
||||
|
||||
$path = sanitize_post_path($path);
|
||||
|
||||
$path_depth = substr_count($path, "/");
|
||||
|
||||
$allowed_ordering = [
|
||||
'path' => true,
|
||||
'path DESC' => true,
|
||||
'created_at' => true,
|
||||
'created_at DESC' => true,
|
||||
'modified_at' => true,
|
||||
'modified_at DESC' => true
|
||||
];
|
||||
|
||||
if(!isset($allowed_ordering[$order_by])) {
|
||||
throw new Exception('Children ordering not allowed');
|
||||
}
|
||||
$order_by = 'post_' . $order_by;
|
||||
|
||||
if($this->debugging) {
|
||||
echo "-> GPC: Getting children for path " . $path;
|
||||
}
|
||||
|
||||
$qry = "
|
||||
SELECT *
|
||||
FROM posts
|
||||
WHERE post_path_depth BETWEEN ? AND ?
|
||||
AND post_path LIKE ?
|
||||
ORDER BY " . $order_by .
|
||||
" LIMIT ?";
|
||||
|
||||
$data = $this->_exec($qry, "iisi",
|
||||
$path_depth + $depth_start, $path_depth + $depth_end,
|
||||
$path.'/%', $limit
|
||||
)->fetch_all(MYSQLI_ASSOC);
|
||||
$outdata = [];
|
||||
|
||||
foreach($data AS $post_element) {
|
||||
$outdata[$post_element['post_path']] =
|
||||
$this->process_postdata($post_element);
|
||||
}
|
||||
|
||||
return $outdata;
|
||||
}
|
||||
|
||||
public function get_post_markdown($id) {
|
||||
$qry =
|
||||
"SELECT post_markdown
|
||||
FROM post_markdown
|
||||
WHERE post_id = ?
|
||||
";
|
||||
|
||||
$data = $this->_exec($qry, "i", $id)->fetch_assoc();
|
||||
|
||||
if(!isset($data)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return $data['post_markdown'];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
205
www/src/db_handler/post.php
Normal file
205
www/src/db_handler/post.php
Normal file
|
@ -0,0 +1,205 @@
|
|||
|
||||
<?php
|
||||
|
||||
class Post implements ArrayAccess {
|
||||
public $handler;
|
||||
|
||||
private $content_html;
|
||||
private $content_markdown;
|
||||
|
||||
public $site_defaults;
|
||||
|
||||
public $data;
|
||||
|
||||
public $html_data;
|
||||
public $raw_data;
|
||||
|
||||
private $_child_posts;
|
||||
|
||||
public static function _generate_404($post_data) {
|
||||
$post_data ??= [
|
||||
'id' => -1,
|
||||
'path' => '/404.md',
|
||||
'title' => '404 Page',
|
||||
'metadata' => [
|
||||
'type' => '404'
|
||||
]
|
||||
];
|
||||
|
||||
return $post_data;
|
||||
}
|
||||
|
||||
public static function _deduce_type($path) {
|
||||
$ext = pathinfo($path, PATHINFO_EXTENSION);
|
||||
|
||||
if(preg_match("/\.(\w+)\.md$/", $path, $ext_match)) {
|
||||
$ext = $ext_match[1];
|
||||
}
|
||||
|
||||
$ext_mapping = [
|
||||
'' => 'directory',
|
||||
'md' => 'text/markdown',
|
||||
'png' => 'image',
|
||||
'jpg' => 'image',
|
||||
'jpeg' => 'image'
|
||||
];
|
||||
|
||||
return $ext_mapping[$ext] ?? '?';
|
||||
}
|
||||
|
||||
public static function _deduce_icon($type) {
|
||||
$icon_mapping = [
|
||||
'' => 'question',
|
||||
'text/markdown' => 'markdown',
|
||||
'blog' => 'markdown',
|
||||
'blog_list' => 'rectangle-list',
|
||||
'directory' => 'folder',
|
||||
'gallery' => 'images',
|
||||
'image' => 'image'
|
||||
];
|
||||
|
||||
return $icon_mapping[$type] ?? 'unknown';
|
||||
}
|
||||
|
||||
function __construct($post_handler, $post_data) {
|
||||
$this->handler = $post_handler;
|
||||
|
||||
$this->content_html = null;
|
||||
$this->content_markdown = null;
|
||||
|
||||
$this->site_defaults = null;
|
||||
|
||||
if(!isset($post_data) or !isset($post_data['id'])) {
|
||||
$post_data = $this->_generate_404($post_data);
|
||||
}
|
||||
|
||||
$data = $post_data;
|
||||
|
||||
$post_data['host'] ??= 'localhost:8081';
|
||||
|
||||
$data['url'] ??= 'https://' . $post_data['host'] . $post_data['path'];
|
||||
|
||||
$data['title'] ??= basename($data['path']);
|
||||
|
||||
$data['tags'] ??= [];
|
||||
|
||||
$data['type'] ??= self::_deduce_type($post_data['path']);
|
||||
|
||||
$data['icon'] ??= self::_deduce_icon($data['type']);
|
||||
|
||||
if(isset($sql_meta['media_url'])) {
|
||||
$data['thumb_url'] ??= $data['media_url'];
|
||||
}
|
||||
|
||||
$data['preview_image'] ??= $data['banners'][0]['src'] ?? null;
|
||||
|
||||
$data['brief'] ??= $data['title'];
|
||||
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function __get($name) {
|
||||
if($name == 'html') {
|
||||
return $this->get_html();
|
||||
}
|
||||
if($name == 'markdown') {
|
||||
return $this->get_markdown();
|
||||
}
|
||||
if($name == 'json') {
|
||||
return $this->to_json();
|
||||
}
|
||||
if($name == 'child_posts') {
|
||||
return $this->get_child_posts();
|
||||
}
|
||||
|
||||
if(isset($this->data[$name])) {
|
||||
return $this->data[$name];
|
||||
}
|
||||
|
||||
if(is_null($this->site_defaults)) {
|
||||
throw new RuntimeException("Post site defaults have not been set properly!");
|
||||
}
|
||||
|
||||
return $this->site_defaults[$name] ?? null;
|
||||
}
|
||||
|
||||
public function offsetGet($offset) : mixed {
|
||||
return $this->__get($offset) ?? null;
|
||||
}
|
||||
public function offsetExists($offset) : bool {
|
||||
if(isset($this->data[$offset])) {
|
||||
return true;
|
||||
}
|
||||
if(isset($this->site_defaults[$offset])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !is_null($this->offsetGet($offset));
|
||||
}
|
||||
public function offsetSet($offset, $value) : void {
|
||||
throw RuntimeError("Setting of post data is not allowed!");
|
||||
}
|
||||
public function offsetUnset($offset) : void {
|
||||
throw RuntimeError("Unsetting of post data is not allowed!");
|
||||
}
|
||||
|
||||
public function get_html() {
|
||||
$this->content_html ??= $this->handler->render_post($this);
|
||||
|
||||
return $this->content_html;
|
||||
}
|
||||
public function get_markdown() {
|
||||
$this->content_markdown ??=
|
||||
$this->handler->get_markdown_for($this);
|
||||
|
||||
return $this->content_markdown;
|
||||
}
|
||||
|
||||
public function get_child_posts(...$search_args) {
|
||||
var_dump($search_args);
|
||||
|
||||
|
||||
if(count($search_args) == 0) {
|
||||
$this->child_posts ??=
|
||||
$this->handler->get_children_for($this);
|
||||
|
||||
return $this->child_posts;
|
||||
}
|
||||
else {
|
||||
return $this->handler->get_children_for($this, ...$search_args);
|
||||
}
|
||||
}
|
||||
|
||||
public function to_array($options = []) {
|
||||
$out_data = $this->data;
|
||||
|
||||
if(isset($options['markdown'])) {
|
||||
$out_data['markdown'] = $this->get_markdown();
|
||||
}
|
||||
if(isset($options['html'])) {
|
||||
$out_data['html'] = $this->get_html();
|
||||
}
|
||||
if(isset($options['children'])) {
|
||||
die();
|
||||
}
|
||||
|
||||
return $out_data;
|
||||
}
|
||||
public function to_json($options = []) {
|
||||
return json_encode($this->to_array($options));
|
||||
}
|
||||
|
||||
|
||||
public function get_parent_post() {
|
||||
$parent_path = dirname($this->data['path']);
|
||||
if($parent_path == '')
|
||||
return null;
|
||||
|
||||
$this->parent_post ??= new PostData($this->handler,
|
||||
$this->handler->get_post_by_path($parent_path));
|
||||
|
||||
return $this->parent_post;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
57
www/src/db_handler/post_handler.php
Normal file
57
www/src/db_handler/post_handler.php
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
require_once 'db_interface.php';
|
||||
require_once 'db_handler/post.php';
|
||||
|
||||
class PostHandler {
|
||||
private $db;
|
||||
private $posts;
|
||||
|
||||
public $markdown_engine;
|
||||
|
||||
function __construct($db_adapter) {
|
||||
$this->db = $db_adapter;
|
||||
$this->posts = [];
|
||||
|
||||
$this->markdown_engine = null;
|
||||
}
|
||||
|
||||
public function get_post($key) {
|
||||
$key = sanitize_post_path($key);
|
||||
|
||||
if(isset($this->posts[$key])) {
|
||||
return $this->posts[$key];
|
||||
}
|
||||
|
||||
$post_data = $this->db->get_postdata($key);
|
||||
$post = null;
|
||||
if(isset($post_data)) {
|
||||
$post = new Post($this, $post_data);
|
||||
}
|
||||
|
||||
$this->posts[$key] = $post;
|
||||
|
||||
return $post;
|
||||
}
|
||||
|
||||
public function get_markdown_for($post) {
|
||||
return $this->db->get_post_markdown($post->id);
|
||||
}
|
||||
|
||||
public function render_post($post) {
|
||||
return $this->markdown_engine($post);
|
||||
}
|
||||
|
||||
public function get_children_for($post, ...$search_opts) {
|
||||
$child_list = $this->db->get_post_children($post->path, ...$search_opts);
|
||||
|
||||
$out_list = [];
|
||||
foreach($child_list as $child_data) {
|
||||
array_push($out_list, new Post($this, $child_data));
|
||||
}
|
||||
|
||||
return $out_list;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
187
www/src/dbtest.php
Normal file
187
www/src/dbtest.php
Normal file
|
@ -0,0 +1,187 @@
|
|||
<?php
|
||||
|
||||
header("content-type: text/plain; charset=UTF-8; imeanit=yes");
|
||||
header("X-Content-Type-Options: nosniff");
|
||||
header('Content-Disposition: inline');
|
||||
|
||||
// Reporting E_NOTICE can be good too (to report uninitialized
|
||||
// variables or catch variable name misspellings ...)
|
||||
error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE);
|
||||
|
||||
$data_time_start = microtime(true);
|
||||
|
||||
require_once '../vendor/autoload.php';
|
||||
|
||||
require_once 'db_handler/mysql_handler.php';
|
||||
require_once 'db_handler/post_handler.php';
|
||||
|
||||
require_once 'fontawesome.php';
|
||||
require_once 'dergdown.php';
|
||||
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
$SERVER_HOST = $_SERVER['HTTP_HOST'];
|
||||
if(!preg_match('/^[\w\.\:]+$/', $SERVER_HOST)) {
|
||||
http_response_code(500);
|
||||
|
||||
echo "Not a valid server host (was " . $SERVER_HOST . ")";
|
||||
die();
|
||||
}
|
||||
|
||||
$SERVER_PREFIX = "https://" . $SERVER_HOST;
|
||||
|
||||
$SITE_CONFIG = Yaml::parseFile('../secrets/' . $SERVER_HOST . '.config.yml');
|
||||
$SITE_CONFIG['uri_prefix'] = $SERVER_PREFIX;
|
||||
$SITE_CONFIG['HTTP_HOST'] = $SERVER_HOST;
|
||||
|
||||
$db_params = $SITE_CONFIG['db'];
|
||||
$db_connection = null;
|
||||
try {
|
||||
if(false !== getenv('MYSQL_HOST')) {
|
||||
$db_connection = mysqli_connect(getenv('MYSQL_HOST'),
|
||||
getenv('MYSQL_USER'), getenv('MYSQL_PASSWORD'),
|
||||
getenv('MYSQL_DATABASE'),
|
||||
getenv('MYSQL_PORT'));
|
||||
}
|
||||
else {
|
||||
$db_connection = mysqli_connect($db_params['host'],
|
||||
$db_params['user'], $db_params['password'],
|
||||
$db_params['database'],
|
||||
$db_params['port']);
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
echo 'Connection failed<br>';
|
||||
echo 'Error number: ' . mysqli_connect_errno() . '<br>';
|
||||
echo 'Error message: ' . mysqli_connect_error() . '<br>';
|
||||
die();
|
||||
}
|
||||
|
||||
$db_connection->execute_query("DELETE FROM posts;");
|
||||
|
||||
$sql_adapter = new MySQLHandler($db_connection, $SERVER_HOST);
|
||||
$adapter = new PostHandler($sql_adapter);
|
||||
|
||||
$sql_adapter->debugging = true;
|
||||
|
||||
function test_accounce($title) {
|
||||
echo "\n\n===========================================
|
||||
_______ ______ _____ _______
|
||||
|__ __| ____|/ ____|__ __|
|
||||
| | | |__ | (___ | | (_)
|
||||
| | | __| \___ \ | |
|
||||
| | | |____ ____) | | | _
|
||||
|_| |______|_____/ |_| (_)
|
||||
|
||||
";
|
||||
echo "==== " . $title . "\n";
|
||||
echo "===========================================\n";
|
||||
}
|
||||
|
||||
function adapter_fetch($post_path) {
|
||||
global $db_connection;
|
||||
global $sql_adapter;
|
||||
|
||||
echo "-> Fetching path " . $post_path . "\n";
|
||||
|
||||
echo json_encode($db_connection->execute_query("SELECT * FROM posts WHERE post_path=?", [
|
||||
$post_path
|
||||
])->fetch_assoc(), JSON_PRETTY_PRINT);
|
||||
|
||||
echo "\n-> Adapter output:\n";
|
||||
|
||||
echo json_encode($sql_adapter->get_postdata($post_path), JSON_PRETTY_PRINT) . "\n";
|
||||
}
|
||||
|
||||
echo "Starting test...\n";
|
||||
|
||||
echo "Trying just a stub...\n";
|
||||
$sql_adapter->stub_postdata_tree('/testing/stubtest/1/2/3.md');
|
||||
echo "Stubbed~\n\n";
|
||||
|
||||
echo "Getting the stub post...\n";
|
||||
|
||||
echo json_encode($sql_adapter->get_postdata('/testing'), JSON_PRETTY_PRINT);
|
||||
|
||||
echo "\n\n";
|
||||
|
||||
test_accounce("Basic postdata setting");
|
||||
|
||||
$sql_adapter->set_postdata([
|
||||
'path' => '/testing/settest/test.md',
|
||||
'title' => 'One heck of a test!',
|
||||
'type' => 'text/markdown',
|
||||
'tags' => [
|
||||
'test',
|
||||
'type:text'
|
||||
],
|
||||
'overridetest' => 'metadata'
|
||||
]);
|
||||
|
||||
echo "\nDone!";
|
||||
|
||||
adapter_fetch('/testing/settest/test.md');
|
||||
|
||||
echo "Done!\n\n";
|
||||
|
||||
test_accounce("Setting post markdown...");
|
||||
$sql_adapter->set_postdata([
|
||||
'path' => '/testing/markdowntest',
|
||||
'markdown' => 'Inline markdown test should work...'
|
||||
]);
|
||||
$post = $sql_adapter->get_postdata('/testing/markdowntest');
|
||||
var_dump($sql_adapter->get_post_markdown($post['id']));
|
||||
|
||||
$sql_adapter->set_post_markdown($post['id'],
|
||||
'This is one hell of a cute test!'
|
||||
);
|
||||
var_dump($sql_adapter->get_post_markdown($post['id']));
|
||||
unset($post);
|
||||
|
||||
test_accounce("Settings inheritance test...");
|
||||
echo "Setting on a parent file...\n";
|
||||
|
||||
$sql_adapter->set_postdata([
|
||||
'path' => '/testing/settest',
|
||||
'settings' => [
|
||||
'nom' => true,
|
||||
'type' => 'frame',
|
||||
'overridetest' => 'settings'
|
||||
]
|
||||
]);
|
||||
|
||||
echo "\nAnd checking if that held!\n";
|
||||
|
||||
adapter_fetch('/testing/settest');
|
||||
adapter_fetch('/testing/settest/test.md');
|
||||
|
||||
test_accounce("Testing getting child posts");
|
||||
|
||||
echo json_encode($sql_adapter->get_post_children('/testing'), JSON_PRETTY_PRINT);
|
||||
|
||||
echo "\n\n------------------------------------------------------\n";
|
||||
|
||||
echo "TEST PHASE: Adapter testing";
|
||||
echo "\n------------------------------------------------------\n\n";
|
||||
|
||||
$post = $adapter->get_post('/testing/markdowntest');
|
||||
echo "Post path is " . $post->path . "\n";
|
||||
echo "Post markdown is " . $post->markdown . "\n";
|
||||
echo $post->to_json();
|
||||
|
||||
echo "\n\n";
|
||||
|
||||
echo $post->to_json([
|
||||
'markdown' => true
|
||||
]);
|
||||
|
||||
test_accounce("Fetching child posts");
|
||||
echo "Root children:\n". json_encode(array_map(function($data) {
|
||||
return $data->to_array();
|
||||
}, $adapter->get_post('/')->child_posts), JSON_PRETTY_PRINT);
|
||||
echo "\n\n";
|
||||
|
||||
echo "Root children, extended:\n" . json_encode(array_map(function($data) {
|
||||
return $data->to_array();
|
||||
}, $adapter->get_post('/')->get_child_posts(depth_end: 3)), JSON_PRETTY_PRINT);
|
||||
echo "\n\n";
|
||||
?>
|
39
www/src/dergdown.php
Normal file
39
www/src/dergdown.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
|
||||
<?php
|
||||
|
||||
use Highlight\Highlighter;
|
||||
|
||||
class Dergdown extends ParsedownExtra
|
||||
{
|
||||
protected $highlighter;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->highlighter = new Highlighter();
|
||||
}
|
||||
|
||||
protected function blockFencedCodeComplete($block)
|
||||
{
|
||||
if (! isset($block['element']['text']['attributes'])) {
|
||||
return $block;
|
||||
}
|
||||
|
||||
$code = $block['element']['text']['text'];
|
||||
$languageClass = $block['element']['text']['attributes']['class'];
|
||||
$language = explode('-', $languageClass);
|
||||
|
||||
try {
|
||||
$highlighted = $this->highlighter->highlight($language[1], $code);
|
||||
$block['element']['text']['attributes']['class'] = vsprintf('%s hljs %s', [
|
||||
$languageClass,
|
||||
$highlighted->language,
|
||||
]);
|
||||
$block['element']['text']['rawHtml'] = $highlighted->value;
|
||||
unset($block['element']['text']['text']);
|
||||
} catch (DomainException $e) {
|
||||
}
|
||||
|
||||
return $block;
|
||||
}
|
||||
}
|
16
www/src/fontawesome.php
Normal file
16
www/src/fontawesome.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
$FONT_AWESOME_ARRAY=[
|
||||
'bars' => '<svg xmlns="http://www.w3.org/2000/svg" class="fa-icn" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"/></svg>',
|
||||
'markdown' => '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="20" class="fa-icn" viewBox="0 0 640 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M593.8 59.1H46.2C20.7 59.1 0 79.8 0 105.2v301.5c0 25.5 20.7 46.2 46.2 46.2h547.7c25.5 0 46.2-20.7 46.1-46.1V105.2c0-25.4-20.7-46.1-46.2-46.1zM338.5 360.6H277v-120l-61.5 76.9-61.5-76.9v120H92.3V151.4h61.5l61.5 76.9 61.5-76.9h61.5v209.2zm135.3 3.1L381.5 256H443V151.4h61.5V256H566z"/></svg>',
|
||||
'image' => '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" class="fa-icn" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M0 96C0 60.7 28.7 32 64 32H448c35.3 0 64 28.7 64 64V416c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V96zM323.8 202.5c-4.5-6.6-11.9-10.5-19.8-10.5s-15.4 3.9-19.8 10.5l-87 127.6L170.7 297c-4.6-5.7-11.5-9-18.7-9s-14.2 3.3-18.7 9l-64 80c-5.8 7.2-6.9 17.1-2.9 25.4s12.4 13.6 21.6 13.6h96 32H424c8.9 0 17.1-4.9 21.2-12.8s3.6-17.4-1.4-24.7l-120-176zM112 192a48 48 0 1 0 0-96 48 48 0 1 0 0 96z"/></svg>',
|
||||
'images' => '<svg xmlns="http://www.w3.org/2000/svg" class="fa-icn" viewBox="0 0 576 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M160 32c-35.3 0-64 28.7-64 64V320c0 35.3 28.7 64 64 64H512c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H160zM396 138.7l96 144c4.9 7.4 5.4 16.8 1.2 24.6S480.9 320 472 320H328 280 200c-9.2 0-17.6-5.3-21.6-13.6s-2.9-18.2 2.9-25.4l64-80c4.6-5.7 11.4-9 18.7-9s14.2 3.3 18.7 9l17.3 21.6 56-84C360.5 132 368 128 376 128s15.5 4 20 10.7zM192 128a32 32 0 1 1 64 0 32 32 0 1 1 -64 0zM48 120c0-13.3-10.7-24-24-24S0 106.7 0 120V344c0 75.1 60.9 136 136 136H456c13.3 0 24-10.7 24-24s-10.7-24-24-24H136c-48.6 0-88-39.4-88-88V120z"/></svg>',
|
||||
'turn-up' => '<svg xmlns="http://www.w3.org/2000/svg" class="fa-icn" viewBox="0 0 384 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M350 177.5c3.8-8.8 2-19-4.6-26l-136-144C204.9 2.7 198.6 0 192 0s-12.9 2.7-17.4 7.5l-136 144c-6.6 7-8.4 17.2-4.6 26s12.5 14.5 22 14.5h88l0 192c0 17.7-14.3 32-32 32H32c-17.7 0-32 14.3-32 32v32c0 17.7 14.3 32 32 32l80 0c70.7 0 128-57.3 128-128l0-192h88c9.6 0 18.2-5.7 22-14.5z"/></svg>',
|
||||
'rectangle-list' => '<svg xmlns="http://www.w3.org/2000/svg" class="fa-icn" viewBox="0 0 576 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M64 80c-8.8 0-16 7.2-16 16V416c0 8.8 7.2 16 16 16H512c8.8 0 16-7.2 16-16V96c0-8.8-7.2-16-16-16H64zM0 96C0 60.7 28.7 32 64 32H512c35.3 0 64 28.7 64 64V416c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V96zm96 64a32 32 0 1 1 64 0 32 32 0 1 1 -64 0zm104 0c0-13.3 10.7-24 24-24H448c13.3 0 24 10.7 24 24s-10.7 24-24 24H224c-13.3 0-24-10.7-24-24zm0 96c0-13.3 10.7-24 24-24H448c13.3 0 24 10.7 24 24s-10.7 24-24 24H224c-13.3 0-24-10.7-24-24zm0 96c0-13.3 10.7-24 24-24H448c13.3 0 24 10.7 24 24s-10.7 24-24 24H224c-13.3 0-24-10.7-24-24zm-72-64a32 32 0 1 1 0-64 32 32 0 1 1 0 64zM96 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg>',
|
||||
'folder-tree' => '<svg xmlns="http://www.w3.org/2000/svg" class="fa-icn" viewBox="0 0 576 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M64 32C64 14.3 49.7 0 32 0S0 14.3 0 32v96V384c0 35.3 28.7 64 64 64H256V384H64V160H256V96H64V32zM288 192c0 17.7 14.3 32 32 32H544c17.7 0 32-14.3 32-32V64c0-17.7-14.3-32-32-32H445.3c-8.5 0-16.6-3.4-22.6-9.4L409.4 9.4c-6-6-14.1-9.4-22.6-9.4H320c-17.7 0-32 14.3-32 32V192zm0 288c0 17.7 14.3 32 32 32H544c17.7 0 32-14.3 32-32V352c0-17.7-14.3-32-32-32H445.3c-8.5 0-16.6-3.4-22.6-9.4l-13.3-13.3c-6-6-14.1-9.4-22.6-9.4H320c-17.7 0-32 14.3-32 32V480z"/></svg>',
|
||||
'folder' => '<svg xmlns="http://www.w3.org/2000/svg" class="fa-icn" height="16" width="16" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z"/></svg>',
|
||||
'folder-open' => '<svg xmlns="http://www.w3.org/2000/svg" class="fa-icn" viewBox="0 0 576 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M88.7 223.8L0 375.8V96C0 60.7 28.7 32 64 32H181.5c17 0 33.3 6.7 45.3 18.7l26.5 26.5c12 12 28.3 18.7 45.3 18.7H416c35.3 0 64 28.7 64 64v32H144c-22.8 0-43.8 12.1-55.3 31.8zm27.6 16.1C122.1 230 132.6 224 144 224H544c11.5 0 22 6.1 27.7 16.1s5.7 22.2-.1 32.1l-112 192C453.9 474 443.4 480 432 480H32c-11.5 0-22-6.1-27.7-16.1s-5.7-22.2 .1-32.1l112-192z"/></svg>',
|
||||
'rss' => '<svg xmlns="http://www.w3.org/2000/svg" height="16" class="fa-icn" width="14" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM96 136c0-13.3 10.7-24 24-24c137 0 248 111 248 248c0 13.3-10.7 24-24 24s-24-10.7-24-24c0-110.5-89.5-200-200-200c-13.3 0-24-10.7-24-24zm0 96c0-13.3 10.7-24 24-24c83.9 0 152 68.1 152 152c0 13.3-10.7 24-24 24s-24-10.7-24-24c0-57.4-46.6-104-104-104c-13.3 0-24-10.7-24-24zm0 120a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg>'
|
||||
];
|
||||
|
||||
?>
|
445
www/src/old_mysql_adapter.php
Normal file
445
www/src/old_mysql_adapter.php
Normal file
|
@ -0,0 +1,445 @@
|
|||
<?php
|
||||
|
||||
class MySQLAdapter {
|
||||
public $raw;
|
||||
|
||||
function __construct($SITE_CONFIG) {
|
||||
$this->SITE_CONFIG = $SITE_CONFIG;
|
||||
|
||||
$db_params = $SITE_CONFIG['db'];
|
||||
|
||||
try {
|
||||
if(false !== getenv('MYSQL_HOST')) {
|
||||
$this->raw = mysqli_connect(getenv('MYSQL_HOST'),
|
||||
getenv('MYSQL_USER'), getenv('MYSQL_PASSWORD'),
|
||||
getenv('MYSQL_DATABASE'),
|
||||
getenv('MYSQL_PORT'));
|
||||
}
|
||||
else {
|
||||
$this->raw = mysqli_connect($db_params['host'],
|
||||
$db_params['user'], $db_params['password'],
|
||||
$db_params['database'],
|
||||
$db_params['port']);
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
echo 'Connection failed<br>';
|
||||
echo 'Error number: ' . mysqli_connect_errno() . '<br>';
|
||||
echo 'Error message: ' . mysqli_connect_error() . '<br>';
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
function _sanitize_path($post_path) {
|
||||
$post_path = chop($post_path, '/');
|
||||
|
||||
if($post_path == "") {
|
||||
return "";
|
||||
}
|
||||
|
||||
if(!preg_match('/^(?:\/[\w-]+)+(?:\.[\w-]+)*$/', $post_path)) {
|
||||
echo "Post path match against " . $post_path . " failed!";
|
||||
die();
|
||||
}
|
||||
|
||||
return $post_path;
|
||||
}
|
||||
|
||||
function _exec($qery, $argtypes = '', ...$args) {
|
||||
$stmt = $this->raw->prepare($qery);
|
||||
if($argtypes != ""){
|
||||
$stmt->bind_param($argtypes, ...$args);
|
||||
}
|
||||
$stmt->execute();
|
||||
|
||||
return $stmt->get_result();
|
||||
}
|
||||
|
||||
function _normalize_post_data($post_data) {
|
||||
$post_data ??= ['found' => null];
|
||||
|
||||
if(isset($post_data['found']) && $post_data['found'] == false) {
|
||||
return $post_data;
|
||||
}
|
||||
|
||||
$post_data["found"] = true;
|
||||
|
||||
$post_data['post_metadata'] = json_decode($post_data["post_metadata"], true) ?? [];
|
||||
$post_data["post_content"] ??= '';
|
||||
|
||||
return $post_data;
|
||||
}
|
||||
|
||||
function _normalize_post_array($post_data) {
|
||||
$post_data ??= [];
|
||||
|
||||
return array_map(function($post) {
|
||||
return $this->_normalize_post_data($post);
|
||||
}, $post_data);
|
||||
}
|
||||
|
||||
function bump_post($post_path, $post_metadata = [], $create_dirs = true) {
|
||||
$post_path = $this->_sanitize_path($post_path);
|
||||
$path_depth = substr_count($post_path, "/");
|
||||
|
||||
if($create_dirs) {
|
||||
$this->make_post_directory(dirname($post_path));
|
||||
}
|
||||
|
||||
$qry = "
|
||||
INSERT INTO posts
|
||||
(host, post_path, post_path_depth, post_metadata, post_content)
|
||||
VALUES
|
||||
( ?, ?, ?, ?, ?) AS new
|
||||
ON DUPLICATE KEY UPDATE post_path=new.post_path;";
|
||||
|
||||
$this->_exec($qry, "ssiss",
|
||||
$this->SITE_CONFIG['HTTP_HOST'],
|
||||
$post_path,
|
||||
$path_depth,
|
||||
json_encode($post_metadata),
|
||||
'');
|
||||
}
|
||||
|
||||
function make_post_directory($directory) {
|
||||
$json_metadata = ["type" => 'directory'];
|
||||
|
||||
while(strlen($directory) > 1) {
|
||||
try {
|
||||
$this->bump_post($directory, $json_metadata, false);
|
||||
}
|
||||
catch(Exception $e) {
|
||||
}
|
||||
|
||||
$directory = dirname($directory);
|
||||
}
|
||||
}
|
||||
|
||||
function log_post_access($post_path, $agent, $referrer, $time) {
|
||||
$post_path = $this->_sanitize_path($post_path);
|
||||
|
||||
$qry = "INSERT INTO path_access_counts
|
||||
(access_time,
|
||||
host, post_path, agent, referrer,
|
||||
path_access_count,
|
||||
path_processing_time)
|
||||
VALUES ( from_unixtime(floor(unix_timestamp(CURRENT_TIMESTAMP) / 300)*300),
|
||||
?, ?, ?, ?, 1, ?
|
||||
) AS new
|
||||
ON DUPLICATE KEY
|
||||
UPDATE path_access_count=path_access_counts.path_access_count+1,
|
||||
path_processing_time=path_access_counts.path_processing_time+new.path_processing_time;
|
||||
";
|
||||
|
||||
$this->_exec($qry, "ssssd", $this->SITE_CONFIG['HTTP_HOST'], $post_path, $agent, $referrer, $time);
|
||||
|
||||
if(preg_match('/^user/', $agent)) {
|
||||
$this->_exec("UPDATE posts SET post_access_count=post_access_count+1 WHERE post_path=? AND host=?", "ss",
|
||||
$post_path, $this->SITE_CONFIG['HTTP_HOST']);
|
||||
}
|
||||
}
|
||||
|
||||
function get_post_access_counters() {
|
||||
$qry = "
|
||||
SELECT host, post_path, agent, path_access_count, path_processing_time
|
||||
FROM path_access_counts
|
||||
WHERE path_last_access_time > ( CURRENT_TIMESTAMP - INTERVAL 10 MINUTE );
|
||||
";
|
||||
|
||||
$data = $this->_exec($qry, "")->fetch_all(MYSQLI_ASSOC);
|
||||
|
||||
$out_data = [];
|
||||
|
||||
foreach($data AS $post_data) {
|
||||
$path = $post_data['post_path'];
|
||||
|
||||
$agent_data = ($out_data[$path] ?? []);
|
||||
|
||||
$agent_data[$post_data['agent']] = [
|
||||
'count' => $post_data['path_access_count'],
|
||||
'time' => round($post_data['path_processing_time'], 6)
|
||||
];
|
||||
|
||||
$out_data[$path] = $agent_data;
|
||||
}
|
||||
|
||||
return $out_data;
|
||||
}
|
||||
|
||||
function get_post_access_counters_line() {
|
||||
$qry = "
|
||||
SELECT host, access_time, post_path, agent, referrer, path_access_count, path_processing_time
|
||||
FROM path_access_counts
|
||||
WHERE access_time < ( CURRENT_TIMESTAMP - INTERVAL 6 MINUTE )
|
||||
ORDER BY access_time DESC;
|
||||
";
|
||||
|
||||
$this->raw->begin_transaction();
|
||||
$top_access_time = null;
|
||||
|
||||
try {
|
||||
$data = $this->_exec($qry, "")->fetch_all(MYSQLI_ASSOC);
|
||||
|
||||
$data_prefix="access_metrics";
|
||||
|
||||
$out_data = "";
|
||||
|
||||
foreach($data AS $post_data) {
|
||||
$top_access_time ??= $post_data['access_time'];
|
||||
|
||||
$path = $post_data['post_path'];
|
||||
if($path == '') {
|
||||
$path = '/';
|
||||
}
|
||||
$out_data .= $data_prefix . ",host=" . $post_data['host'] . ",agent=".$post_data['agent'];
|
||||
$out_data .= ",path=".$path.",referrer=".$post_data['referrer'];
|
||||
|
||||
$out_data .= " access_sum=" . $post_data['path_access_count'] . ",time_sum=" . $post_data['path_processing_time'];
|
||||
$out_data .= " " . strtotime($post_data['access_time']) . "000000000\n";
|
||||
}
|
||||
|
||||
|
||||
$this->_exec("DELETE FROM path_access_counts WHERE access_time <= ?", "s", $top_access_time);
|
||||
|
||||
$this->raw->commit();
|
||||
return $out_data;
|
||||
|
||||
} catch (\Throwable $th) {
|
||||
$this->raw->rollback();
|
||||
|
||||
throw $th;
|
||||
}
|
||||
}
|
||||
|
||||
function reset_post_settings_cache($post_path) {
|
||||
$post_path = $this->_sanitize_path($post_path);
|
||||
|
||||
$this->_exec("
|
||||
UPDATE posts
|
||||
SET post_settings_cache=NULL
|
||||
WHERE host = ? AND post_path LIKE ?;
|
||||
", "ss", $this->SITE_CONFIG['HTTP_HOST'], $post_path . "%");
|
||||
}
|
||||
|
||||
function escape_tag($tag) {
|
||||
return preg_replace_callback('/[\WZ]/', function($match) {
|
||||
return "Z" . ord($match[0]);
|
||||
}, strtolower($tag));
|
||||
}
|
||||
|
||||
function escape_search_tag($tag) {
|
||||
preg_match("/^([\+\-]?)(.*?)(\*?)$/", $tag, $matches);
|
||||
|
||||
if(!isset($matches[1])) {
|
||||
echo "Problem with tag!";
|
||||
var_dump($tag);
|
||||
}
|
||||
|
||||
return $matches[1] . $this->escape_tag($matches[2]) . $matches[3];
|
||||
}
|
||||
|
||||
function update_post_search_data($post_path, $post_tags) {
|
||||
$post_tags []= "path:" . $post_path;
|
||||
$post_tags []= "host:" . $this->SITE_CONFIG['HTTP_HOST'];
|
||||
|
||||
$post_tags = array_unique($post_tags);
|
||||
$post_tags = array_map(function($val) {
|
||||
return $this->escape_tag($val);
|
||||
}, $post_tags);
|
||||
|
||||
asort($post_tags);
|
||||
$post_tags = join(' ', $post_tags);
|
||||
|
||||
$qry = "
|
||||
INSERT INTO posts
|
||||
( host, post_path, post_tags )
|
||||
VALUES
|
||||
( ?, ?, ? ) AS new
|
||||
ON DUPLICATE KEY
|
||||
UPDATE post_tags=new.post_tags;
|
||||
";
|
||||
|
||||
$this->_exec($qry, "sss",
|
||||
$this->SITE_CONFIG['HTTP_HOST'], $post_path, $post_tags);
|
||||
}
|
||||
|
||||
function perform_post_search($taglist, $order = null, $limit = 20, $page = 0) {
|
||||
$allowed_ordering = [
|
||||
"post_create_time"
|
||||
];
|
||||
|
||||
$qry = "
|
||||
SELECT *
|
||||
FROM posts
|
||||
WHERE MATCH(post_tags) AGAINST (? IN BOOLEAN MODE)
|
||||
";
|
||||
|
||||
if(!is_array($taglist)) {
|
||||
$taglist = explode(' ', $taglist);
|
||||
}
|
||||
|
||||
$taglist []= '+host:' . $this->SITE_CONFIG['HTTP_HOST'];
|
||||
$taglist = array_unique($taglist);
|
||||
$taglist = array_map(function($key) {
|
||||
return $this->escape_search_tag($key);
|
||||
}, $taglist);
|
||||
|
||||
$taglist = implode(' ', $taglist);
|
||||
|
||||
if(isset($order) and in_array($order, $allowed_ordering)) {
|
||||
$qry = $qry . " ORDER BY " . $order;
|
||||
}
|
||||
|
||||
$qry = $qry . " LIMIT ? OFFSET ?";
|
||||
|
||||
$search_results = $this->_exec($qry, "sii", $taglist, $limit, $limit * $page)->fetch_all(MYSQLI_ASSOC);
|
||||
$search_results = [
|
||||
"query_string" => $taglist,
|
||||
"results" => $this->_normalize_post_array($search_results)
|
||||
];
|
||||
|
||||
return $search_results;
|
||||
}
|
||||
|
||||
function update_or_create_post($post_path, $post_metadata, $post_content) {
|
||||
$post_path = $this->_sanitize_path($post_path);
|
||||
$path_depth = substr_count($post_path, "/");
|
||||
|
||||
$this->make_post_directory(dirname($post_path));
|
||||
|
||||
$this->reset_post_settings_cache($post_path);
|
||||
|
||||
$qry = "
|
||||
INSERT INTO posts
|
||||
(host, post_path, post_path_depth, post_metadata, post_content)
|
||||
VALUES
|
||||
( ?, ?, ?, ?, ?) AS new
|
||||
ON DUPLICATE KEY
|
||||
UPDATE post_metadata=new.post_metadata,
|
||||
post_content=new.post_content,
|
||||
post_update_time=CURRENT_TIMESTAMP;";
|
||||
|
||||
$this->_exec($qry, "ssiss",
|
||||
$this->SITE_CONFIG['HTTP_HOST'],
|
||||
$post_path,
|
||||
$path_depth,
|
||||
json_encode($post_metadata),
|
||||
$post_content);
|
||||
|
||||
$this->update_post_search_data($post_path, $post_metadata['tags'] ?? []);
|
||||
}
|
||||
|
||||
function get_settings_for_path($post_path) {
|
||||
$post_path = $this->_sanitize_path($post_path);
|
||||
|
||||
$post_settings = $this->_exec("
|
||||
SELECT post_path, post_settings_cache
|
||||
FROM posts
|
||||
WHERE post_path = ? AND host = ?
|
||||
", "ss", $post_path, $this->SITE_CONFIG['HTTP_HOST'])->fetch_assoc();
|
||||
|
||||
if(!isset($post_settings)) {
|
||||
return [];
|
||||
}
|
||||
if(isset($post_settings['post_settings_cache'])) {
|
||||
return json_decode($post_settings['post_settings_cache'], true);
|
||||
}
|
||||
|
||||
$parent_settings = [];
|
||||
if($post_path != "") {
|
||||
$parent_settings = $this->get_settings_for_path(dirname($post_path));
|
||||
}
|
||||
|
||||
$post_settings = [];
|
||||
$post_metadata = $this->_exec("
|
||||
SELECT post_path, post_metadata
|
||||
FROM posts
|
||||
WHERE post_path = ? AND host = ?
|
||||
", "ss", $post_path, $this->SITE_CONFIG['HTTP_HOST'])->fetch_assoc();
|
||||
|
||||
if(isset($post_metadata['post_metadata'])) {
|
||||
$post_metadata = json_decode($post_metadata['post_metadata'], true);
|
||||
|
||||
if(isset($post_metadata['settings'])) {
|
||||
$post_settings = $post_metadata['settings'];
|
||||
}
|
||||
}
|
||||
|
||||
$post_settings = array_merge($parent_settings, $post_settings);
|
||||
|
||||
$this->_exec("UPDATE posts SET post_settings_cache=? WHERE post_path=?", "ss",
|
||||
json_encode($post_settings), $post_path);
|
||||
|
||||
return $post_settings;
|
||||
}
|
||||
|
||||
function get_post_by_path($post_path,
|
||||
$with_subposts = false, $with_settings = true) {
|
||||
|
||||
$qry = "SELECT *
|
||||
FROM posts
|
||||
WHERE post_path = ? AND host = ?
|
||||
";
|
||||
|
||||
$post_path = $this->_sanitize_path($post_path);
|
||||
|
||||
$post_data = $this->_exec($qry, "ss", $post_path, $this->SITE_CONFIG['HTTP_HOST'])->fetch_assoc();
|
||||
|
||||
$post_data ??= ['found' => false];
|
||||
$post_data['post_path'] = $post_path;
|
||||
$post_data['parent_path'] = dirname($post_path);
|
||||
|
||||
$post_data = $this->_normalize_post_data($post_data);
|
||||
|
||||
if(!$post_data['found']) {
|
||||
return $post_data;
|
||||
}
|
||||
|
||||
if($with_subposts) {
|
||||
$post_data['subposts'] = $this->get_subposts_by_path($post_path);
|
||||
}
|
||||
if($with_settings) {
|
||||
$post_data['settings'] = $this->get_settings_for_path($post_path);
|
||||
}
|
||||
|
||||
return $post_data;
|
||||
}
|
||||
|
||||
function get_markdown_for_id($id) {
|
||||
$qry = "SELECT post_content
|
||||
FROM posts
|
||||
WHERE post_id = ? AND host = ?
|
||||
";
|
||||
|
||||
$post_data = $this->_exec($qry, "is", $id,
|
||||
$this->SITE_CONFIG['HTTP_HOST']
|
||||
)->fetch_assoc();
|
||||
|
||||
return $post_data['post_content'];
|
||||
}
|
||||
|
||||
function get_subposts_by_path($path) {
|
||||
global $sql;
|
||||
|
||||
$path = $this->_sanitize_path($path);
|
||||
|
||||
$path_depth = substr_count($path, "/");
|
||||
|
||||
$qry = "SELECT post_path, post_metadata, post_update_time
|
||||
FROM posts
|
||||
WHERE
|
||||
host = ?
|
||||
AND (post_path LIKE CONCAT(?,'/%'))
|
||||
AND post_path_depth = ?
|
||||
ORDER BY post_path ASC
|
||||
LIMIT 50";
|
||||
|
||||
$post_data = $this->_exec($qry, "ssi", $this->SITE_CONFIG['HTTP_HOST'],
|
||||
$path, $path_depth+1)->fetch_all(MYSQLI_ASSOC);
|
||||
|
||||
$post_data = $this->_normalize_post_array($post_data);
|
||||
|
||||
return $post_data;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
346
www/src/old_router.php
Normal file
346
www/src/old_router.php
Normal file
|
@ -0,0 +1,346 @@
|
|||
<?php
|
||||
|
||||
$data_time_start = microtime(true);
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
|
||||
require_once 'db_handler/mysql_handler.php';
|
||||
require_once 'db_handler/post_handler.php';
|
||||
require_once 'post.php';
|
||||
|
||||
require_once 'fontawesome.php';
|
||||
require_once 'dergdown.php';
|
||||
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
$SERVER_HOST = $_SERVER['HTTP_HOST'];
|
||||
if(!preg_match('/^[\w\.]+$/', $SERVER_HOST)) {
|
||||
die();
|
||||
}
|
||||
|
||||
$SERVER_PREFIX = "https://" . $SERVER_HOST;
|
||||
|
||||
$SITE_CONFIG = Yaml::parseFile('secrets/' . $SERVER_HOST . '.config.yml');
|
||||
$SITE_CONFIG['uri_prefix'] = $SERVER_PREFIX;
|
||||
$SITE_CONFIG['HTTP_HOST'] = $SERVER_HOST;
|
||||
|
||||
$db_params = $SITE_CONFIG['db'];
|
||||
$db_connection = null;
|
||||
try {
|
||||
if(false !== getenv('MYSQL_HOST')) {
|
||||
$db_connection = mysqli_connect(getenv('MYSQL_HOST'),
|
||||
getenv('MYSQL_USER'), getenv('MYSQL_PASSWORD'),
|
||||
getenv('MYSQL_DATABASE'),
|
||||
getenv('MYSQL_PORT'));
|
||||
}
|
||||
else {
|
||||
$db_connection = mysqli_connect($db_params['host'],
|
||||
$db_params['user'], $db_params['password'],
|
||||
$db_params['database'],
|
||||
$db_params['port']);
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
echo 'Connection failed<br>';
|
||||
echo 'Error number: ' . mysqli_connect_errno() . '<br>';
|
||||
echo 'Error message: ' . mysqli_connect_error() . '<br>';
|
||||
die();
|
||||
}
|
||||
|
||||
$sql_adapter = new MySQLHandler($db_connection, $SERVER_HOST);
|
||||
$adapter = new PostHandler($sql_adapter);
|
||||
|
||||
$loader = new \Twig\Loader\FilesystemLoader(['./templates', './user_content']);
|
||||
|
||||
$twig = new \Twig\Environment($loader,[
|
||||
'debug' => true,
|
||||
'cache' => 'twig_cache'
|
||||
]);
|
||||
$twig->addExtension(new Twig\Extra\Markdown\MarkdownExtension());
|
||||
|
||||
use Twig\Extra\Markdown\DefaultMarkdown;
|
||||
use Twig\Extra\Markdown\MarkdownRuntime;
|
||||
use Twig\RuntimeLoader\RuntimeLoaderInterface;
|
||||
|
||||
function dergdown_to_html($text) {
|
||||
$Parsedown = new Dergdown();
|
||||
|
||||
return $Parsedown->text($text);
|
||||
}
|
||||
function post_to_html($post) {
|
||||
return dergdown_to_html($post->markdown);
|
||||
}
|
||||
PostData::$markdown_engine = "post_to_html";
|
||||
|
||||
function deduce_user_agent() {
|
||||
$real_agent=$_SERVER['HTTP_USER_AGENT'];
|
||||
|
||||
if(preg_match('/(Googlebot|\w*Google\w*)/', $real_agent, $match)) {
|
||||
return "bot/google/" . $match[1];
|
||||
}
|
||||
elseif(preg_match('/(Mozilla|Chrome|Chromium)/', $real_agent, $match)) {
|
||||
return "user/" . $match[1];
|
||||
}
|
||||
else {
|
||||
return "unidentified";
|
||||
}
|
||||
}
|
||||
|
||||
function log_and_die($path, $die_code = 0, $referrer = null) {
|
||||
global $data_time_start;
|
||||
global $adapter;
|
||||
|
||||
$data_time_end = microtime(true);
|
||||
|
||||
if(!isset($referrer)) {
|
||||
$referrer = 'magic';
|
||||
|
||||
if(isset($_SERVER['HTTP_REFERER'])) {
|
||||
$referrer = parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST);
|
||||
}
|
||||
}
|
||||
|
||||
$adapter->log_post_access($path,
|
||||
deduce_user_agent(),
|
||||
$referrer,
|
||||
$data_time_end - $data_time_start);
|
||||
|
||||
die($die_code);
|
||||
}
|
||||
|
||||
$twig->addRuntimeLoader(new class implements RuntimeLoaderInterface {
|
||||
public function load($class) {
|
||||
if (MarkdownRuntime::class === $class) {
|
||||
return new MarkdownRuntime(new DefaultMarkdown());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function render_twig($template, $args = []) {
|
||||
global $twig;
|
||||
global $FONT_AWESOME_ARRAY;
|
||||
global $SITE_CONFIG;
|
||||
|
||||
$args['fa'] = $FONT_AWESOME_ARRAY;
|
||||
|
||||
$post = $args['post'] ?? [];
|
||||
$settings = $post['settings'] ?? [];
|
||||
$meta = $post['post_metadata'] ?? [];
|
||||
|
||||
$args['banner'] ??= $settings['banners'] ?? $SITE_CONFIG['banners'];
|
||||
|
||||
$args['og'] = array_merge([
|
||||
"site_name" => $SITE_CONFIG['opengraph']['site_name'],
|
||||
"title" => $meta['title'] ?? $SITE_CONFIG['opengraph']['site_name'],
|
||||
"url" => $_SERVER['REQUEST_URI'],
|
||||
"type" => "article",
|
||||
"description" => $meta['description']
|
||||
?? $settings['description']
|
||||
?? $SITE_CONFIG['opengraph']['description']
|
||||
], $args['og'] ?? []);
|
||||
|
||||
if(($meta['type'] ?? '') == 'image') {
|
||||
$args['og']['image'] ??= $meta['media_file'];
|
||||
$args['og']['type'] = "image";
|
||||
}
|
||||
|
||||
$args['og']['image'] ??= $args['banner'][0]["src"];
|
||||
|
||||
$args['banner'] = json_encode($args['banner']);
|
||||
|
||||
$args['site_config'] = $SITE_CONFIG;
|
||||
|
||||
$args['age_gate'] = (!isset($_COOKIE['AgeConfirmed'])) && isset($SITE_CONFIG['age_gate']);
|
||||
|
||||
$args['content_html'] ??= dergdown_to_html($post['post_content'] ?? '');
|
||||
|
||||
echo $twig->render($template, $args);
|
||||
}
|
||||
|
||||
function try_render_ajax($SURI) {
|
||||
global $adapter;
|
||||
|
||||
$match = null;
|
||||
preg_match('/^\/ajax\/([^\/]+)(.*)$/', $SURI, $match);
|
||||
|
||||
if(!isset($match)) {
|
||||
die();
|
||||
}
|
||||
|
||||
$post = $adapter->get_post_by_path($match[2]);
|
||||
$subposts = $adapter->get_subposts_by_path($match[2]);
|
||||
|
||||
echo render_twig('ajax/' . $match[1] . '.html', [
|
||||
"post" => $post,
|
||||
"subposts" => $subposts
|
||||
]);
|
||||
}
|
||||
|
||||
function try_render_post($SURI) {
|
||||
global $adapter;
|
||||
|
||||
$post = $adapter->get_post_by_path($SURI);
|
||||
|
||||
if(!$post['found']) {
|
||||
echo render_twig('post_types/rrror.html',[
|
||||
"error_code" => '404 Hoard not found!',
|
||||
"error_description" => "Well, we searched
|
||||
far and wide for `" . $SURI . "` but
|
||||
somehow it must have gotten lost... Sorry!",
|
||||
"post" => array_merge($post, [
|
||||
"post_metadata" => ["title" => "404 ???"]
|
||||
])
|
||||
]);
|
||||
|
||||
log_and_die('/404', referrer: ($_SERVER['HTTP_REFERER'] ?? 'magic'));
|
||||
}
|
||||
|
||||
switch($post['post_metadata']['type']) {
|
||||
case 'directory':
|
||||
if(preg_match('/^(.*[^\/])((?:#.*)?)$/', $SURI, $match)) {
|
||||
header('Location: ' . $match[1] . '/' . $match[2]);
|
||||
|
||||
die();
|
||||
}
|
||||
|
||||
echo render_twig('post_types/directory.html', [
|
||||
"post" => $post,
|
||||
"subposts" => $adapter->get_subposts_by_path($SURI)
|
||||
]);
|
||||
|
||||
break;
|
||||
|
||||
case 'blog':
|
||||
case 'text/markdown':
|
||||
echo render_twig('post_types/markdown.html', [
|
||||
"post" => $post
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'gallery':
|
||||
if(preg_match('/^(.*[^\/])((?:#.*)?)$/', $SURI, $match)) {
|
||||
header('Location: ' . $match[1] . '/' . $match[2]);
|
||||
|
||||
die();
|
||||
}
|
||||
|
||||
$search_query = $post['post_metadata']['search_tags'] ??
|
||||
('+type:image +path:' . $post['post_path'] . '/*');
|
||||
|
||||
$search_result = $adapter->perform_post_search($search_query);
|
||||
|
||||
echo render_twig('post_types/gallery.html', [
|
||||
"post" => $post,
|
||||
"subposts" => $adapter->get_subposts_by_path($SURI),
|
||||
"gallery_images" => $search_result['results']
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'blog_list':
|
||||
if(preg_match('/^(.*[^\/])((?:#.*)?)$/', $SURI, $match)) {
|
||||
header('Location: ' . $match[1] . '/' . $match[2]);
|
||||
|
||||
die();
|
||||
}
|
||||
|
||||
$search_query = $post['post_metadata']['search_tags'] ??
|
||||
('+type:blog +path:' . $post['post_path'] . '/*');
|
||||
|
||||
$search_result = $adapter->perform_post_search($search_query);
|
||||
|
||||
$search_result = array_map(function($key) {
|
||||
$post = new PostData($adapter, $key);
|
||||
return $post->data;
|
||||
}, $search_result['results']);
|
||||
|
||||
echo render_twig('post_types/blog_list.html', [
|
||||
"post" => $post,
|
||||
"subposts" => $adapter->get_subposts_by_path($SURI),
|
||||
"blog_posts" => $search_result
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'image':
|
||||
echo render_twig('post_types/image.html', [
|
||||
"post" => $post,
|
||||
]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function generate_website($SURI) {
|
||||
global $adapter;
|
||||
global $FONT_AWESOME_ARRAY;
|
||||
|
||||
if(preg_match('/^\/api\/admin/', $SURI)) {
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$user_api_key = '';
|
||||
if(isset($_GET['api_key'])) {
|
||||
$user_api_key = $_GET['api_key'];
|
||||
}
|
||||
if(isset($_POST['api_key'])) {
|
||||
$user_api_key = $_POST['api_key'];
|
||||
}
|
||||
|
||||
if($user_api_key != file_get_contents('secrets/api_admin_key')) {
|
||||
http_response_code(401);
|
||||
|
||||
echo json_encode([
|
||||
"authorized" => false
|
||||
]);
|
||||
|
||||
log_and_die('/api/401');
|
||||
}
|
||||
|
||||
if($SURI = '/api/admin/upload') {
|
||||
$adapter->handle_upload($_POST['post_path'], $_FILES['post_data']['tmp_name']);
|
||||
|
||||
echo json_encode(["ok" => true]);
|
||||
}
|
||||
} elseif(preg_match('/^\/api/', $SURI)) {
|
||||
if($SURI == '/api/post_counters') {
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($adapter->get_post_access_counters());
|
||||
} elseif($SURI == '/api/metrics') {
|
||||
header('Content-Type: application/line');
|
||||
echo $adapter->get_post_access_counters_line();
|
||||
} elseif(preg_match('/^\/api\/posts(.*)$/', $SURI, $match)) {
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$post = new PostData($adapter, $adapter->get_post_by_path($match[1]));
|
||||
echo $post->to_json(with_markdown: true, with_html: true);
|
||||
|
||||
} elseif(preg_match('/^\/api\/subposts(.*)$/', $SURI, $match)) {
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(get_subposts($match[1]));
|
||||
} elseif($SURI == '/api/upload') {
|
||||
|
||||
echo $twig->render('upload.html');
|
||||
} elseif($SURI == '/api/search') {
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($adapter->perform_post_search($_GET['search_query']));
|
||||
}
|
||||
} elseif(preg_match('/^\/ajax\//', $SURI)) {
|
||||
try_render_ajax($SURI);
|
||||
} elseif(preg_match('/^\/feed(?:\/(rss|atom)(.*))?$/', $SURI, $match)) {
|
||||
$feed = $adapter->get_laminas_feed($match[2] ?? '/', $match[1] ?? 'rss');
|
||||
|
||||
header('Content-Type: application/xml');
|
||||
header('Cache-Control: max-age=1800');
|
||||
header('Etag: W/"' . $SURI . '/' . strtotime($feed['feed_ts']) . '"');
|
||||
|
||||
echo $feed['feed'];
|
||||
} else {
|
||||
try_render_post($SURI);
|
||||
}
|
||||
}
|
||||
|
||||
$URL_PATH = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
||||
generate_website($URL_PATH);
|
||||
|
||||
log_and_die($URL_PATH);
|
||||
|
||||
?>
|
19
www/src/router.php
Normal file
19
www/src/router.php
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
$data_time_start = microtime(true);
|
||||
|
||||
require_once '../vendor/autoload.php';
|
||||
|
||||
require_once 'setup_site_config.php';
|
||||
require_once 'setup_db.php';
|
||||
|
||||
require_once 'fontawesome.php';
|
||||
require_once 'dergdown.php';
|
||||
|
||||
require_once 'setup_twig.php';
|
||||
|
||||
$PARSED_URL = parse_url($_SERVER['REQUEST_URI']);
|
||||
|
||||
require_once 'serve_post.php';
|
||||
|
||||
?>
|
46
www/src/serve_post.php
Normal file
46
www/src/serve_post.php
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
require_once 'fontawesome.php';
|
||||
|
||||
$post = $adapter->get_post($PARSED_URL['path']);
|
||||
if(isset($post)) {
|
||||
$post->site_defaults = $SITE_CONFIG['site_defaults'];
|
||||
}
|
||||
|
||||
function render_root_template($template, $args = []) {
|
||||
global $twig;
|
||||
global $FONT_AWESOME_ARRAY;
|
||||
global $SITE_CONFIG;
|
||||
|
||||
$args['fontawesome'] = $FONT_AWESOME_ARRAY;
|
||||
$args['page'] ??= $SITE_CONFIG['site_defaults'];
|
||||
|
||||
$page = $args['page'];
|
||||
|
||||
$args['opengraph'] = [
|
||||
"site_name" => $page['site_name'] ?? 'UNSET SITE NAME',
|
||||
"title" => $page['title'] ?? 'UNSET TITLE',
|
||||
"url" => $page['url'] ?? 'UNSET URL',
|
||||
"description" => $page['description'] ?? 'UNSET DESCRIPTION'
|
||||
];
|
||||
|
||||
$args['banners'] = json_encode($page['banners'] ?? []);
|
||||
|
||||
$args['age_gate'] = (!isset($_COOKIE['AgeConfirmed']))
|
||||
&& isset($SITE_CONFIG['age_gate']);
|
||||
|
||||
echo $twig->render($template, $args);
|
||||
}
|
||||
|
||||
render_root_template('root.html', [
|
||||
'page' => $post
|
||||
]);
|
||||
die();
|
||||
|
||||
if(!isset($post)) {
|
||||
render_404();
|
||||
die();
|
||||
}
|
||||
|
||||
|
||||
?>
|
43
www/src/setup_db.php
Normal file
43
www/src/setup_db.php
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
require_once 'db_handler/mysql_handler.php';
|
||||
require_once 'db_handler/post_handler.php';
|
||||
|
||||
$db_params = $SITE_CONFIG['db'];
|
||||
$db_connection = null;
|
||||
try {
|
||||
if(false !== getenv('MYSQL_HOST')) {
|
||||
$db_connection = mysqli_connect(getenv('MYSQL_HOST'),
|
||||
getenv('MYSQL_USER'), getenv('MYSQL_PASSWORD'),
|
||||
getenv('MYSQL_DATABASE'),
|
||||
getenv('MYSQL_PORT'));
|
||||
}
|
||||
else {
|
||||
$db_connection = mysqli_connect($db_params['host'],
|
||||
$db_params['user'], $db_params['password'],
|
||||
$db_params['database'],
|
||||
$db_params['port']);
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
echo 'Connection failed<br>';
|
||||
echo 'Error number: ' . mysqli_connect_errno() . '<br>';
|
||||
echo 'Error message: ' . mysqli_connect_error() . '<br>';
|
||||
die();
|
||||
}
|
||||
|
||||
$sql_adapter = new MySQLHandler($db_connection, $SERVER_HOST);
|
||||
$adapter = new PostHandler($sql_adapter);
|
||||
|
||||
require_once 'dergdown.php';
|
||||
|
||||
function dergdown_to_html($text) {
|
||||
$Parsedown = new Dergdown();
|
||||
|
||||
return $Parsedown->text($text);
|
||||
}
|
||||
function post_to_html($post) {
|
||||
return dergdown_to_html($post->markdown);
|
||||
}
|
||||
$adapter->markdown_engine = "post_to_html";
|
||||
|
||||
?>
|
19
www/src/setup_site_config.php
Normal file
19
www/src/setup_site_config.php
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
$SERVER_HOST = $_SERVER['HTTP_HOST'];
|
||||
if(!preg_match('/^[\w\.\:]+$/', $SERVER_HOST)) {
|
||||
http_response_code(500);
|
||||
|
||||
echo "Not a valid server host (was " . $SERVER_HOST . ")";
|
||||
die();
|
||||
}
|
||||
|
||||
$SERVER_PREFIX = "https://" . $SERVER_HOST;
|
||||
|
||||
$SITE_CONFIG = Yaml::parseFile('../secrets/' . $SERVER_HOST . '.config.yml');
|
||||
$SITE_CONFIG['uri_prefix'] = $SERVER_PREFIX;
|
||||
$SITE_CONFIG['HTTP_HOST'] = $SERVER_HOST;
|
||||
|
||||
?>
|
24
www/src/setup_twig.php
Normal file
24
www/src/setup_twig.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
$loader = new \Twig\Loader\FilesystemLoader(['../templates']);
|
||||
|
||||
$twig = new \Twig\Environment($loader,[
|
||||
'debug' => true,
|
||||
'cache' => 'twig_cache'
|
||||
]);
|
||||
$twig->addExtension(new Twig\Extra\Markdown\MarkdownExtension());
|
||||
|
||||
use Twig\Extra\Markdown\DefaultMarkdown;
|
||||
use Twig\Extra\Markdown\MarkdownRuntime;
|
||||
use Twig\RuntimeLoader\RuntimeLoaderInterface;
|
||||
|
||||
$twig->addRuntimeLoader(new class implements RuntimeLoaderInterface {
|
||||
public function load($class) {
|
||||
if (MarkdownRuntime::class === $class) {
|
||||
return new MarkdownRuntime(new DefaultMarkdown());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
?>
|
Loading…
Add table
Add a link
Reference in a new issue