diff --git a/.gitignore b/.gitignore index 253937d..d26e73a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -tmp/temp.tdata sitemap.xml node_modules/ npm-debug.log @@ -8,3 +7,5 @@ yarn-error.log !.yarn/plugins !.yarn/sdks !.yarn/versions +assets/bundle/* +!assets/bundle/.gitkeep \ No newline at end of file diff --git a/admin/admin.php b/admin/admin.php index 856ba63..c75b667 100644 --- a/admin/admin.php +++ b/admin/admin.php @@ -142,9 +142,4 @@ $admin_logs = AdminLog::with('user') - - \ No newline at end of file + \ No newline at end of file diff --git a/api/ajax_pastes.php b/api/ajax_pastes.php index 68bb9d2..226ad4a 100644 --- a/api/ajax_pastes.php +++ b/api/ajax_pastes.php @@ -14,6 +14,7 @@ $pastes = Paste::with([ ])->select(['id', 'user_id', 'title'])->get(); header('Content-Type: application/json; charset=UTF-8'); + echo json_encode(['data' => $pastes->map(function($paste) { return [ 'id' => $paste->id, diff --git a/api/public.pastes.php b/api/public.pastes.php deleted file mode 100644 index 383d646..0000000 --- a/api/public.pastes.php +++ /dev/null @@ -1,390 +0,0 @@ - intval($request['draw']), - "recordsTotal" => intval($recordsTotal), - "recordsFiltered" => intval($recordsFiltered), - "data" => self::data_output($columns2, $data) - ); - } - - - /** - * Connect to the database - * - * @param array $sql_details SQL server connection details array, with the - * properties: - * * host - host name - * * db - database name - * * user - user name - * * pass - user password - * @return resource Database connection handle - */ - static function sql_connect($sql_details) { - try { - $db = @new PDO( - "mysql:host={$sql_details['host']};dbname={$sql_details['db']}", - $sql_details['user'], - $sql_details['pass'], - array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION) - ); - } catch (PDOException $e) { - self::fatal( - "An error occurred while connecting to the database. " . - "The error reported by the server was: " . $e->getMessage() - ); - } - - return $db; - } - - - /** - * Execute an SQL query on the database - * - * @param resource $db Database handler - * @param array $bindings Array of PDO binding values from bind() to be - * used for safely escaping strings. Note that this can be given as the - * SQL query string if no bindings are required. - * @param string $sql SQL query to execute. - * @return array Result from the query (all rows) - */ - static function sql_exec($db, $bindings, $sql = null) { - // Argument shifting - if ($sql === null) { - $sql = $bindings; - } - - $stmt = $db->prepare($sql); - //echo $sql; - - // Bind parameters - if (is_array($bindings)) { - for ($i = 0, $ien = count($bindings); $i < $ien; $i++) { - $binding = $bindings[$i]; - $stmt->bindValue($binding['key'], $binding['val'], $binding['type']); - } - } - - // Execute - try { - $stmt->execute(); - } catch (PDOException $e) { - self::fatal("An SQL error occurred: " . $e->getMessage()); - } - return $stmt->fetchAll(); - } - - static function Ssql_exec($db, $bindings, $sql = null) { - // Argument shifting - if ($sql === null) { - $sql = $bindings; - } - - $stmt = $db->prepare($sql); - - // Bind parameters - if (is_array($bindings)) { - for ($i = 0, $ien = count($bindings); $i < $ien; $i++) { - $binding = $bindings[$i]; - $stmt->bindValue($binding['key'], $binding['val'], $binding['type']); - } - } - - // Execute - try { - $stmt->execute(); - } catch (PDOException $e) { - self::fatal("An SQL error occurred: " . $e->getMessage()); - } - $loop = '0'; - - while ($arr = $stmt->fetch(PDO::FETCH_ASSOC)) { - $result[$loop]['id'] = $arr['id']; - $result[$loop]['title'] = html_entity_decode($arr['title']); - $result[$loop]['member'] = $arr['member']; - $result[$loop]['tagsys'] = $arr['tagsys']; - $date_time = strtotime($arr['date']); - $result[$loop]['date'] = date(DATE_ISO8601, $date_time); - $myupdate = $arr['now_time']; - $result[$loop]['now_time'] = date(DATE_ISO8601, $myupdate); - - - $loop = $loop + 1; - } - - // Return all - return $result; - } - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Internal methods - */ - - /** - * Throw a fatal error. - * - * This writes out an error message in a JSON string which DataTables will - * see and show to the user in the browser. - * - * @param string $msg Message to send to the client - */ - static function fatal($msg) { - echo json_encode(array( - "error" => $msg - )); - - exit(0); - } - - /** - * Create a PDO binding key which can be used for escaping variables safely - * when executing a query with sql_exec() - * - * @param array &$a Array of bindings - * @param * $val Value to bind - * @param int $type PDO field type - * @return string Bound key to be used in the SQL where this parameter - * would be used. - */ - static function bind(&$a, $val, $type) { - $key = ':binding_' . count($a); - - $a[] = array( - 'key' => $key, - 'val' => $val, - 'type' => $type - ); - - return $key; - } - - - /** - * Pull a particular property from each assoc. array in a numeric array, - * returning and array of the property values from each item. - * - * @param array $a Array to get data from - * @param string $prop Property to read - * @return array Array of property values - */ - static function pluck($a, $prop) { - $out = array(); - - for ($i = 0, $len = count($a); $i < $len; $i++) { - $out[] = $a[$i][$prop]; - } - - return $out; - } -} - diff --git a/api/tags_autocomplete.php b/api/tags_autocomplete.php index 606afe9..88c7863 100644 --- a/api/tags_autocomplete.php +++ b/api/tags_autocomplete.php @@ -25,5 +25,4 @@ $results = Tag::select('name') array_push($tags, ['name' => $tag_name]); - echo json_encode($tags); diff --git a/assets/bundle.min.js.map b/assets/bundle.min.js.map deleted file mode 100644 index 4bc476b..0000000 --- a/assets/bundle.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"bundle.min.js","sources":["../js/dom.js","../js/tag_input.js","../js/data_tables.js","../js/main.js"],"sourcesContent":["const $ = function(selector) {\n return document.querySelector(selector);\n};\n\nconst $$ = function(selector) {\n return document.querySelectorAll(selector) || [];\n};\n\nconst makeEl = function(html) {\n const template = document.createElement('template');\n\n template.innerHTML = html.trim();\n\n return template.content.firstChild;\n};\n\nconst clearEl = function(el) {\n while (el.firstChild) {\n el.removeChild(el.firstChild);\n }\n};\n\nconst escape = function(unsafe) {\n return unsafe\n .replace(/&/g, \"&\")\n .replace(//g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\n\nexport { $, $$, makeEl, clearEl, escape };","import { makeEl, escape } from \"./dom\";\n\nclass TagsInput {\n constructor(element, options = {}) {\n this.element = element;\n this.tags = [];\n this.options = options\n\n this.maxTags = options.maxTags || 10;\n this.inputNode = null;\n this.containerNode = null;\n }\n\n attach() {\n this.element.style.display = 'none';\n\n this.containerNode = makeEl('
');\n this.inputNode = makeEl('');\n this.containerNode.appendChild(this.inputNode);\n\n this.element.parentNode.insertBefore(this.containerNode, this.element.nextSibling);\n\n /* Load existing tags from input */\n if (this.element.value) {\n for (const tag of this.element.value.split(',')) {\n this.addTag(tag);\n }\n }\n\n /* Handle addition and removal of tags via key-presses */\n this.containerNode.addEventListener('keydown', this._handleInputKeyUp.bind(this));\n\n /* Handle deletions by clicking the delete button */\n this.containerNode.addEventListener('click', this._handleContainerClick.bind(this));\n }\n\n detach() {\n this.tags.clear();\n this.containerNode.remove();\n this.element.style.display = 'inline-block';\n }\n\n updateHiddenInputValue() {\n this.element.value = this.tags.join(',');\n }\n\n deleteTagNode(node) {\n this.tags.splice(this.tags.indexOf(node.dataset.value.toLowerCase()), 1);\n node.remove();\n\n /* Below the limit? Make sure the input is enabled. */\n if (this.tags.length < this.maxTags) {\n this.inputNode.disabled = false;\n }\n }\n\n addTag(tagValue) {\n tagValue = tagValue.trim();\n\n /* Tag value is probably not empty and we don't already have the same tag. */\n if (tagValue !== '' && this.tags.indexOf(tagValue.toLowerCase()) === -1) {\n this.tags.push(tagValue.toLowerCase());\n\n this.inputNode.parentNode.insertBefore(\n makeEl('' + escape(tagValue) + ''),\n this.inputNode\n );\n\n /* Too many tags, disable the input for now. */\n if (this.tags.length >= this.maxTags) {\n this.inputNode.disabled = true;\n }\n }\n }\n\n _handleInputKeyUp(evt) {\n let tagValue = this.inputNode.value;\n\n if (evt.key === 'Backspace' && tagValue === '') {\n // Remove the child\n if (this.inputNode.previousSibling) {\n this.deleteTagNode(this.inputNode.previousSibling);\n\n this.updateHiddenInputValue();\n }\n } else if (evt.key === ',') {\n this.addTag(tagValue);\n\n this.inputNode.value = ''\n this.updateHiddenInputValue();\n\n evt.preventDefault();\n }\n }\n\n _handleContainerClick(evt) {\n if (evt.target && evt.target.classList.contains('delete')) {\n this.deleteTagNode(evt.target.closest('.tag'));\n this.updateHiddenInputValue();\n }\n }\n}\n\nexport { TagsInput };\n","import { makeEl, clearEl } from \"./dom\";\n\n\nclass SimplePaginator {\n constructor(element) {\n this.element = element;\n }\n\n attach(pageCallback) {\n this.element.addEventListener('click', evt => {\n if (evt.target && evt.target.classList.contains('paginator__button')) {\n pageCallback(+evt.target.dataset.page);\n }\n });\n }\n\n update(totalRecords, perPage, currentPage) {\n clearEl(this.element);\n\n /* First and last page in existence */\n const firstPage = 0;\n const lastPage = Math.floor(totalRecords / perPage); // ish?\n const numPagesToShow = 2;\n\n /* First and last page the main paginator will show */\n const firstPageShow = (currentPage - firstPage) < numPagesToShow ? firstPage : ((currentPage - numPagesToShow < 0) ? currentPage : currentPage - numPagesToShow);\n const lastPageShow = (firstPageShow + numPagesToShow) > lastPage ? lastPage : (firstPageShow + numPagesToShow + numPagesToShow);\n\n /* Whether to show the first and last pages in existence at the ends of the paginator */\n const showFirstPage = (Math.abs(firstPage - currentPage)) > (numPagesToShow);\n const showLastPage = (Math.abs(lastPage - currentPage)) > (numPagesToShow);\n\n\n const prevButtonDisabled = currentPage === firstPage ? 'disabled' : ''\n\n /* Previous button */\n this.element.appendChild(makeEl(\n `Previous`\n ));\n\n /* First page button */\n if (showFirstPage) {\n this.element.appendChild(makeEl(\n `${firstPage}`\n ));\n this.element.appendChild(makeEl(``));\n }\n\n /* \"window\" buttons */\n for (let i = firstPageShow; i <= lastPageShow; i++) {\n const selected = (i === currentPage ? 'paginator__button--selected' : '');\n this.element.appendChild(makeEl(\n `${i}`\n ));\n }\n\n /* Last page button */\n if (showLastPage) {\n this.element.appendChild(makeEl(``));\n this.element.appendChild(makeEl(\n `${lastPage}`\n ));\n }\n\n const nextButtonDisabled = currentPage === lastPage ? 'disabled' : ''\n /* Next button */\n this.element.appendChild(makeEl(\n `Next`\n ));\n }\n}\n\nclass DataTable {\n constructor(element, options) {\n this.element = element;\n this.container = element.parentElement;\n this.options = options;\n\n this.ajaxCallback = options.ajaxCallback;\n this.data = [];\n\n this.totalRecords = -1;\n this.perPage = 20;\n this.currentPage = 0;\n\n this.paginator = new SimplePaginator(this.container.querySelector('.paginator'));\n\n }\n\n attach() {\n this.paginator.attach(this._updatePage.bind(this));\n this._loadEntries();\n }\n\n /* Load the requested data from the server, and when done, update the DOM. */\n _loadEntries() {\n new Promise(this.ajaxCallback)\n .then(this._updateEntries.bind(this));\n }\n\n /* Update the DOM to reflect the current state of the data we have loaded */\n _updateEntries(data) {\n this.data = data.data;\n this.totalRecords = this.data.length;\n\n const bodyElement = this.element.querySelector('tbody');\n clearEl(bodyElement);\n\n const firstIndex = (this.perPage * this.currentPage);\n const lastIndex = (firstIndex + this.perPage) > this.totalRecords ? this.totalRecords : (firstIndex + this.perPage);\n\n\n for (let i = firstIndex; i < lastIndex; i++) {\n const rowElem = makeEl(this.options.rowCallback(this.data[i]));\n rowElem.classList.add(i % 2 === 0 ? 'odd' : 'even');\n\n bodyElement.appendChild(rowElem);\n }\n\n this.paginator.update(this.totalRecords, this.perPage, this.currentPage);\n }\n\n _updatePage(n) {\n this.currentPage = n;\n this.paginator.update(this.totalRecords, this.perPage, this.currentPage);\n this._updateEntries({data: this.data});\n }\n\n _updateSort(field, direction) {\n\n }\n}\n\nexport { DataTable };\n","import { $$, escape } from './dom';\nimport { TagsInput } from \"./tag_input\";\nimport { DataTable } from \"./data_tables\";\n\nconst setupSite = function() {\n Array.prototype.forEach.call($$('.js-tag-input'), (el) => {\n new TagsInput(el).attach();\n });\n\n if (document.querySelector('#archive')) {\n const table = new DataTable(document.querySelector('#archive'), {\n ajaxCallback: (resolve) => {\n fetch('/api/ajax_pastes.php')\n .then(r => r.json())\n .then(resolve);\n },\n rowCallback: (rowData) => {\n const tags = rowData.tags.map((tagData) => {\n let tagColorClass;\n if (tagData.name.indexOf('nsfw') !== -1) {\n tagColorClass = 'is-danger';\n } else if (tagData.name.indexOf('safe') !== -1) {\n tagColorClass = 'is-success';\n } else if (tagData.name.indexOf('/') !== -1) {\n tagColorClass = 'is-primary';\n } else {\n tagColorClass = 'is-info';\n }\n\n return `\n ${escape(tagData.name)}\n `;\n }).join('');\n\n return `\n ${escape(rowData.title)}\n ${escape(rowData.author)}\n ${tags}\n `;\n }\n });\n table.attach();\n }\n};\n\nif (document.readyState !== 'loading') {\n setupSite();\n} else {\n document.addEventListener('DOMContentLoaded', setupSite);\n}\n"],"names":["makeEl","html","template","document","createElement","innerHTML","trim","content","firstChild","clearEl","el","removeChild","escape","unsafe","replace","TagsInput","element","options","tags","maxTags","inputNode","containerNode","style","display","appendChild","this","parentNode","insertBefore","nextSibling","value","split","tag","addTag","addEventListener","_handleInputKeyUp","bind","_handleContainerClick","clear","remove","join","node","splice","indexOf","dataset","toLowerCase","length","disabled","tagValue","push","evt","key","previousSibling","deleteTagNode","updateHiddenInputValue","preventDefault","target","classList","contains","closest","SimplePaginator","pageCallback","page","totalRecords","perPage","currentPage","lastPage","Math","floor","firstPageShow","lastPageShow","showFirstPage","abs","showLastPage","prevButtonDisabled","i","selected","nextButtonDisabled","DataTable","container","parentElement","ajaxCallback","data","paginator","querySelector","attach","_updatePage","_loadEntries","Promise","then","_updateEntries","bodyElement","firstIndex","lastIndex","rowElem","rowCallback","add","update","n","field","direction","setupSite","selector","Array","prototype","forEach","call","querySelectorAll","resolve","fetch","r","json","rowData","map","tagData","tagColorClass","name","slug","id","title","author","readyState"],"mappings":"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"} \ No newline at end of file diff --git a/includes/captchabg/index.php b/assets/bundle/.gitkeep similarity index 100% rename from includes/captchabg/index.php rename to assets/bundle/.gitkeep diff --git a/assets/bundle.js b/assets/bundle/bundle.js similarity index 100% rename from assets/bundle.js rename to assets/bundle/bundle.js diff --git a/assets/bundle.min.js b/assets/bundle/bundle.min.js similarity index 100% rename from assets/bundle.min.js rename to assets/bundle/bundle.min.js diff --git a/assets/bundle/bundle.min.js.map b/assets/bundle/bundle.min.js.map new file mode 100644 index 0000000..5060b20 --- /dev/null +++ b/assets/bundle/bundle.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"bundle.min.js","sources":["../../js/dom.js","../../js/tag_input.js","../../js/data_tables.js","../../js/main.js"],"sourcesContent":["const $ = function(selector) {\n return document.querySelector(selector);\n};\n\nconst $$ = function(selector) {\n return document.querySelectorAll(selector) || [];\n};\n\nconst makeEl = function(html) {\n const template = document.createElement('template');\n\n template.innerHTML = html.trim();\n\n return template.content.firstChild;\n};\n\nconst clearEl = function(el) {\n while (el.firstChild) {\n el.removeChild(el.firstChild);\n }\n};\n\nconst escape = function(unsafe) {\n return unsafe\n .replace(/&/g, \"&\")\n .replace(//g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\n\nexport { $, $$, makeEl, clearEl, escape };","import { makeEl, escape } from \"./dom\";\n\nclass TagsInput {\n constructor(element, options = {}) {\n this.element = element;\n this.tags = [];\n this.options = options\n\n this.maxTags = options.maxTags || 10;\n this.inputNode = null;\n this.containerNode = null;\n }\n\n attach() {\n this.element.style.display = 'none';\n\n this.containerNode = makeEl('
');\n this.inputNode = makeEl('');\n this.containerNode.appendChild(this.inputNode);\n\n this.element.parentNode.insertBefore(this.containerNode, this.element.nextSibling);\n\n /* Load existing tags from input */\n if (this.element.value) {\n for (const tag of this.element.value.split(',')) {\n this.addTag(tag);\n }\n }\n\n /* Handle addition and removal of tags via key-presses */\n this.containerNode.addEventListener('keydown', this._handleInputKeyUp.bind(this));\n\n /* Handle deletions by clicking the delete button */\n this.containerNode.addEventListener('click', this._handleContainerClick.bind(this));\n }\n\n detach() {\n this.tags.clear();\n this.containerNode.remove();\n this.element.style.display = 'inline-block';\n }\n\n updateHiddenInputValue() {\n this.element.value = this.tags.join(',');\n }\n\n deleteTagNode(node) {\n this.tags.splice(this.tags.indexOf(node.dataset.value.toLowerCase()), 1);\n node.remove();\n\n /* Below the limit? Make sure the input is enabled. */\n if (this.tags.length < this.maxTags) {\n this.inputNode.disabled = false;\n }\n }\n\n addTag(tagValue) {\n tagValue = tagValue.trim();\n\n /* Tag value is probably not empty and we don't already have the same tag. */\n if (tagValue !== '' && this.tags.indexOf(tagValue.toLowerCase()) === -1) {\n this.tags.push(tagValue.toLowerCase());\n\n this.inputNode.parentNode.insertBefore(\n makeEl('' + escape(tagValue) + ''),\n this.inputNode\n );\n\n /* Too many tags, disable the input for now. */\n if (this.tags.length >= this.maxTags) {\n this.inputNode.disabled = true;\n }\n }\n }\n\n _handleInputKeyUp(evt) {\n let tagValue = this.inputNode.value;\n\n if (evt.key === 'Backspace' && tagValue === '') {\n // Remove the child\n if (this.inputNode.previousSibling) {\n this.deleteTagNode(this.inputNode.previousSibling);\n\n this.updateHiddenInputValue();\n }\n } else if (evt.key === ',') {\n this.addTag(tagValue);\n\n this.inputNode.value = ''\n this.updateHiddenInputValue();\n\n evt.preventDefault();\n }\n }\n\n _handleContainerClick(evt) {\n if (evt.target && evt.target.classList.contains('delete')) {\n this.deleteTagNode(evt.target.closest('.tag'));\n this.updateHiddenInputValue();\n }\n }\n}\n\nexport { TagsInput };\n","import { makeEl, clearEl } from \"./dom\";\n\n\nclass SimplePaginator {\n constructor(element) {\n this.element = element;\n }\n\n attach(pageCallback) {\n this.element.addEventListener('click', evt => {\n if (evt.target && evt.target.classList.contains('paginator__button')) {\n pageCallback(+evt.target.dataset.page);\n }\n });\n }\n\n update(totalRecords, perPage, currentPage) {\n clearEl(this.element);\n\n /* First and last page in existence */\n const firstPage = 0;\n const lastPage = Math.floor(totalRecords / perPage); // ish?\n const numPagesToShow = 2;\n\n /* First and last page the main paginator will show */\n const firstPageShow = (currentPage - firstPage) < numPagesToShow ? firstPage : ((currentPage - numPagesToShow < 0) ? currentPage : currentPage - numPagesToShow);\n const lastPageShow = (firstPageShow + numPagesToShow) > lastPage ? lastPage : (firstPageShow + numPagesToShow + numPagesToShow);\n\n /* Whether to show the first and last pages in existence at the ends of the paginator */\n const showFirstPage = (Math.abs(firstPage - currentPage)) > (numPagesToShow);\n const showLastPage = (Math.abs(lastPage - currentPage)) > (numPagesToShow);\n\n\n const prevButtonDisabled = currentPage === firstPage ? 'disabled' : ''\n\n /* Previous button */\n this.element.appendChild(makeEl(\n `Previous`\n ));\n\n /* First page button */\n if (showFirstPage) {\n this.element.appendChild(makeEl(\n `${firstPage}`\n ));\n this.element.appendChild(makeEl(``));\n }\n\n /* \"window\" buttons */\n for (let i = firstPageShow; i <= lastPageShow; i++) {\n const selected = (i === currentPage ? 'paginator__button--selected' : '');\n this.element.appendChild(makeEl(\n `${i}`\n ));\n }\n\n /* Last page button */\n if (showLastPage) {\n this.element.appendChild(makeEl(``));\n this.element.appendChild(makeEl(\n `${lastPage}`\n ));\n }\n\n const nextButtonDisabled = currentPage === lastPage ? 'disabled' : ''\n /* Next button */\n this.element.appendChild(makeEl(\n `Next`\n ));\n }\n}\n\nclass DataTable {\n constructor(element, options) {\n this.element = element;\n this.container = element.parentElement;\n this.options = options;\n\n this.ajaxCallback = options.ajaxCallback;\n this.data = [];\n\n this.totalRecords = -1;\n this.perPage = 20;\n this.currentPage = 0;\n\n this.paginator = new SimplePaginator(this.container.querySelector('.paginator'));\n\n }\n\n attach() {\n this.paginator.attach(this._updatePage.bind(this));\n this._loadEntries();\n }\n\n /* Load the requested data from the server, and when done, update the DOM. */\n _loadEntries() {\n new Promise(this.ajaxCallback)\n .then(this._updateEntries.bind(this));\n }\n\n /* Update the DOM to reflect the current state of the data we have loaded */\n _updateEntries(data) {\n this.data = data.data;\n this.totalRecords = this.data.length;\n\n const bodyElement = this.element.querySelector('tbody');\n clearEl(bodyElement);\n\n const firstIndex = (this.perPage * this.currentPage);\n const lastIndex = (firstIndex + this.perPage) > this.totalRecords ? this.totalRecords : (firstIndex + this.perPage);\n\n\n for (let i = firstIndex; i < lastIndex; i++) {\n const rowElem = makeEl(this.options.rowCallback(this.data[i]));\n rowElem.classList.add(i % 2 === 0 ? 'odd' : 'even');\n\n bodyElement.appendChild(rowElem);\n }\n\n this.paginator.update(this.totalRecords, this.perPage, this.currentPage);\n }\n\n _updatePage(n) {\n this.currentPage = n;\n this.paginator.update(this.totalRecords, this.perPage, this.currentPage);\n this._updateEntries({data: this.data});\n }\n\n _updateSort(field, direction) {\n\n }\n}\n\nexport { DataTable };\n","import { $$, escape } from './dom';\nimport { TagsInput } from \"./tag_input\";\nimport { DataTable } from \"./data_tables\";\n\nconst setupSite = function() {\n Array.prototype.forEach.call($$('.js-tag-input'), (el) => {\n new TagsInput(el).attach();\n });\n\n if (document.querySelector('#archive')) {\n const table = new DataTable(document.querySelector('#archive'), {\n ajaxCallback: (resolve) => {\n fetch('/api/ajax_pastes.php')\n .then(r => r.json())\n .then(resolve);\n },\n rowCallback: (rowData) => {\n const tags = rowData.tags.map((tagData) => {\n let tagColorClass;\n if (tagData.name.indexOf('nsfw') !== -1) {\n tagColorClass = 'is-danger';\n } else if (tagData.name.indexOf('safe') !== -1) {\n tagColorClass = 'is-success';\n } else if (tagData.name.indexOf('/') !== -1) {\n tagColorClass = 'is-primary';\n } else {\n tagColorClass = 'is-info';\n }\n\n return `\n ${escape(tagData.name)}\n `;\n }).join('');\n\n return `\n ${escape(rowData.title)}\n ${escape(rowData.author)}\n ${tags}\n `;\n }\n });\n table.attach();\n }\n};\n\nif (document.readyState !== 'loading') {\n setupSite();\n} else {\n document.addEventListener('DOMContentLoaded', setupSite);\n}\n"],"names":["makeEl","html","template","document","createElement","innerHTML","trim","content","firstChild","clearEl","el","removeChild","escape","unsafe","replace","TagsInput","element","options","tags","maxTags","inputNode","containerNode","style","display","appendChild","this","parentNode","insertBefore","nextSibling","value","split","tag","addTag","addEventListener","_handleInputKeyUp","bind","_handleContainerClick","clear","remove","join","node","splice","indexOf","dataset","toLowerCase","length","disabled","tagValue","push","evt","key","previousSibling","deleteTagNode","updateHiddenInputValue","preventDefault","target","classList","contains","closest","SimplePaginator","pageCallback","page","totalRecords","perPage","currentPage","lastPage","Math","floor","firstPageShow","lastPageShow","showFirstPage","abs","showLastPage","prevButtonDisabled","i","selected","nextButtonDisabled","DataTable","container","parentElement","ajaxCallback","data","paginator","querySelector","attach","_updatePage","_loadEntries","Promise","then","_updateEntries","bodyElement","firstIndex","lastIndex","rowElem","rowCallback","add","update","n","field","direction","setupSite","selector","Array","prototype","forEach","call","querySelectorAll","resolve","fetch","r","json","rowData","map","tagData","tagColorClass","name","slug","id","title","author","readyState"],"mappings":"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"} \ No newline at end of file diff --git a/includes/fonts/LMS Pretty Pony.ttf b/assets/fonts/LMS Pretty Pony.ttf similarity index 100% rename from includes/fonts/LMS Pretty Pony.ttf rename to assets/fonts/LMS Pretty Pony.ttf diff --git a/includes/fonts/PonyvilleMedium0.4.ttf b/assets/fonts/PonyvilleMedium0.4.ttf similarity index 100% rename from includes/fonts/PonyvilleMedium0.4.ttf rename to assets/fonts/PonyvilleMedium0.4.ttf diff --git a/includes/fonts/SigmarOne.ttf b/assets/fonts/SigmarOne.ttf similarity index 100% rename from includes/fonts/SigmarOne.ttf rename to assets/fonts/SigmarOne.ttf diff --git a/includes/fonts/captcha_code.otf b/assets/fonts/captcha_code.otf similarity index 100% rename from includes/fonts/captcha_code.otf rename to assets/fonts/captcha_code.otf diff --git a/includes/fonts/times_new_yorker.ttf b/assets/fonts/times_new_yorker.ttf similarity index 100% rename from includes/fonts/times_new_yorker.ttf rename to assets/fonts/times_new_yorker.ttf diff --git a/includes/captchabg/text1.png b/assets/img/captcha/text1.png similarity index 100% rename from includes/captchabg/text1.png rename to assets/img/captcha/text1.png diff --git a/includes/captchabg/text2.png b/assets/img/captcha/text2.png similarity index 100% rename from includes/captchabg/text2.png rename to assets/img/captcha/text2.png diff --git a/includes/captchabg/text3.png b/assets/img/captcha/text3.png similarity index 100% rename from includes/captchabg/text3.png rename to assets/img/captcha/text3.png diff --git a/doc/nginx.conf b/doc/nginx.conf new file mode 100644 index 0000000..889f184 --- /dev/null +++ b/doc/nginx.conf @@ -0,0 +1,76 @@ +# Request limit zone to help mitigate attacks +limit_req_zone $binary_remote_addr zone=ip:10m rate=5r/s; + +# Cleartext listener for LetsEncrypt and HTTPS redirects. +server { + listen 80; + + server_name ponepaste.org; + + location ^~ /.well-known/acme-challenge/ { + root /var/www/letsencrypt/; + } + + location / { + return 301 https://ponepaste.org$request_uri; + } +} + +server { + listen 443 ssl; + listen [::]:443 ssl; + + # SSL Configuration + ssl_certificate /etc/letsencrypt/live/ponepaste.org/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/ponepaste.org/privkey.pem; + add_header Strict-Transport-Security "max-age=63072000" always; + + # ModSecurity config; optional, but recommended. + modsecurity on; + modsecurity_rules_file /etc/modsecurity/modsecurity.conf; + + root /srv/http/ponepaste.org; + index index.html index.php; + + server_name ponepaste.org; + + # Rewrites for pretty URLs + rewrite ^/([0-9]+) /paste.php?id=$1 last; + rewrite ^/page/([a-zA-Z0-9]+)/? /pages.php?page=$1 last; + rewrite ^/archive /archive.php last; + rewrite ^/discover /discover.php last; + rewrite ^/profile /profile.php last; + rewrite ^/user/([^/]+)/?$ /user.php?user=$1 last; + rewrite ^/user/([^/]+)/([^/]+)/?$ /user.php?user=$1&q=$2 last; + rewrite ^/contact /contact.php last; + rewrite ^/download/(.*)$ /paste.php?download&id=$1 last; + rewrite ^/raw/(.*)$ /paste.php?raw&id=$1 last; + rewrite ^/embed/(.*)$ /paste.php?embed&id=$1 last; + rewrite ^/report /report.php last; + rewrite ^/event /event.php last; + + location ~* \.(jpg|jpeg|png|gif|ico|css|js) { + add_header "Cache-Control" "public"; + expires 1h; + } + + location / { + try_files $uri $uri/ =404; + } + + location ~ \.php$ { + limit_req zone=ip burst=10 delay=8; + include snippets/fastcgi-php.conf; + + fastcgi_pass unix:/run/php/php-fpm.sock; + } + + # Deny directories that should not be publicly accessible. + location ~ (/doc|/tmp|/includes|/config|/.git|/.ht|/js|/node_modules).* { + deny all; + } + + location ~ /\.ht { + deny all; + } +} diff --git a/includes/captcha.php b/includes/captcha.php index 79f9bbc..d28c967 100644 --- a/includes/captcha.php +++ b/includes/captcha.php @@ -5,8 +5,8 @@ use JetBrains\PhpStorm\ArrayShape; #[ArrayShape(['code' => "mixed|string", 'image_src' => "string"])] function captcha($color, $mode, $mul, $allowed) : array { - $bg_path = dirname(__FILE__) . '/captchabg/'; - $font_path = dirname(__FILE__) . '/fonts/'; + $bg_path = '/assets/img/captcha/'; + $font_path = '/assets/font/'; if ($mul == "on") { $captcha_config = array( diff --git a/includes/fonts/index.php b/includes/fonts/index.php deleted file mode 100644 index e69de29..0000000 diff --git a/includes/functions.php b/includes/functions.php index e9e5d99..7e788d7 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -111,9 +111,6 @@ function formatBytes($size, $precision = 2) { return round(pow(1024, $base - floor($base)), $precision) . ' ' . $suffixes[floor($base)]; } - - - function friendlyDateDifference(DateTime $lesser, DateTime $greater) : string { $delta = $greater->diff($lesser, true); diff --git a/rollup.config.js b/rollup.config.js index d2bbe6c..ba2ed03 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -7,11 +7,11 @@ export default { input: 'js/main.js', output: [ { - file: 'assets/bundle.js', + file: 'assets/bundle/bundle.js', format: 'esm' }, { - file: 'assets/bundle.min.js', + file: 'assets/bundle/bundle.min.js', format: 'esm', plugins: [getBabelOutputPlugin({ presets: ['@babel/preset-env'] }), terser()], sourcemap: true diff --git a/rules.php b/rules.php index ac53304..ccd4f0c 100644 --- a/rules.php +++ b/rules.php @@ -7,4 +7,3 @@ require_once('includes/functions.php'); $page_template = 'rules'; $page_title = 'Rules'; require_once('theme/' . $default_theme . '/common.php'); - diff --git a/tmp/.htaccess b/tmp/.htaccess deleted file mode 100644 index 3418e55..0000000 --- a/tmp/.htaccess +++ /dev/null @@ -1 +0,0 @@ -deny from all \ No newline at end of file