diff options
| -rw-r--r-- | http/public/do/bcrypt.php | 57 | ||||
| -rw-r--r-- | http/public/scripts/feed-condense-content.js | 67 | ||||
| -rw-r--r-- | http/src/css/base.uppcss | 18 | ||||
| -rw-r--r-- | http/src/css/feed.uppcss | 38 |
4 files changed, 95 insertions, 85 deletions
diff --git a/http/public/do/bcrypt.php b/http/public/do/bcrypt.php new file mode 100644 index 0000000..a61dd9e --- /dev/null +++ b/http/public/do/bcrypt.php @@ -0,0 +1,57 @@ +<?php + +const BCRYPT_COST = 12; + +$user = $_SERVER['PHP_AUTH_USER'] ?? null; +$pass = $_SERVER['PHP_AUTH_PW'] ?? null; + +// Fallback for CGI / FastCGI SAPIs that don't populate PHP_AUTH_*. +// Apache: add "CGIPassAuth On" (2.4.13+) or a RewriteRule to expose +// HTTP_AUTHORIZATION; nginx+php-fpm passes it through by default. +if ($user === null || $pass === null) { + $header = $_SERVER['HTTP_AUTHORIZATION'] + ?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] + ?? null; + + if (is_string($header) && preg_match('/^Basic\s+(.+)$/i', $header, $m) === 1) { + $decoded = base64_decode($m[1], true); + + // Split on the FIRST colon only: passwords may contain colons. + if ($decoded !== false && str_contains($decoded, ':')) { + [$user, $pass] = explode(':', $decoded, 2); + } + } +} + +if (!is_string($user) || $user === '' || !is_string($pass) || $pass === '') { + header('WWW-Authenticate: Basic realm="Restricted", charset="UTF-8"'); + http_response_code(401); + header('Content-Type: text/plain; charset=utf-8'); + header('Cache-Control: no-store'); + echo 'authentication required'; + exit; +} + +if (strlen($pass) > 72) { + http_response_code(422); + header('Content-Type: text/plain; charset=utf-8'); + header('Cache-Control: no-store'); + echo 'password exceeds the 72-byte bcrypt limit'; + exit; +} + +$hash = password_hash($pass, PASSWORD_BCRYPT, ['cost' => BCRYPT_COST]); + +if ($hash === false) { + http_response_code(500); + header('Content-Type: text/plain; charset=utf-8'); + header('Cache-Control: no-store'); + echo 'hashing failed'; + exit; +} + +header('Content-Type: text/plain; charset=utf-8'); +header('Cache-Control: no-store, no-cache, must-revalidate'); +header('Pragma: no-cache'); + +echo $user . ':' . $hash; diff --git a/http/public/scripts/feed-condense-content.js b/http/public/scripts/feed-condense-content.js deleted file mode 100644 index 984dbce..0000000 --- a/http/public/scripts/feed-condense-content.js +++ /dev/null @@ -1,67 +0,0 @@ -const expandedClass = "e-content--expanded"; - -export function toggleContentExpanded(id) { - const content = document.querySelector(`.e-content[data-content-id="${id}"]`); - const buttonBefore = document.querySelector( - `button:has(+ .e-content[data-content-id="${id}"])`, - ); - const buttonAfter = document.querySelector( - `.e-content[data-content-id="${id}"] + button`, - ); - - if (!content) { - throw new Error(`No content found with ID: ${id}`); - } - const classes = content.getAttribute("class").split(" "); - const wasExpanded = classes.includes(expandedClass); - if (wasExpanded) { - const newClasses = classes.filter((c) => c != expandedClass); - content.setAttribute("class", newClasses.join(" ")); - } else { - content.setAttribute("class", [...classes, expandedClass].join(" ")); - } - - if (wasExpanded) { - if (!buttonBefore) { - console.warn("Tried to remove 'Read less' button but could not find it"); - } else { - buttonBefore.remove(); - } - if (!buttonAfter) { - console.warn( - "Tried to toggle 'Read less' button to say 'Read more' but could not find it", - ); - } else { - buttonAfter.textContent = "Read more"; - } - } else { - if (!buttonAfter) { - console.warn( - "Tried to toggle 'Read more' button to say 'Read less' but could not find it", - ); - } else { - buttonAfter.textContent = "Read less"; - } - if (buttonBefore) { - buttonBefore.remove(); - } - const newButtonBefore = createExpandCondenseButton(id, "Read less"); - content.insertAdjacentElement("beforebegin", newButtonBefore); - } -} - -export function addExpandItemButtons() { - for (const content of document.querySelectorAll(".e-content")) { - const contentId = crypto.randomUUID(); - content.setAttribute("data-content-id", contentId); - const button = createExpandCondenseButton(contentId, "Read more"); - content.insertAdjacentElement("afterend", button); - } -} - -function createExpandCondenseButton(contentId, initialText) { - const button = document.createElement("button"); - button.textContent = initialText; - button.addEventListener("click", () => toggleContentExpanded(contentId)); - return button; -} diff --git a/http/src/css/base.uppcss b/http/src/css/base.uppcss index e67e5c0..b7a0e83 100644 --- a/http/src/css/base.uppcss +++ b/http/src/css/base.uppcss @@ -73,9 +73,9 @@ --font-size-sm: 1rem; --font-size-base: 1.125rem; - --font-size-md: 1.5rem; - --font-size-lg: 2rem; - --font-size-xl: 3rem; + --font-size-md: 1.3rem; + --font-size-lg: 1.6rem; + --font-size-xl: 2rem; --spacing-block-xs: 0.5rem; --spacing-block-sm: 1.75rem; @@ -89,6 +89,16 @@ --spacing-inline-xl: 6rem; } +@media (min-width: 60rem) { + :root { + --font-size-sm: 1rem; + --font-size-base: 1.25rem; + --font-size-md: 1.55rem; + --font-size-lg: 1.9rem; + --font-size-xl: 2.3rem; + } +} + /** Light theme */ @media (prefers-color-scheme: light) { :root { @@ -204,7 +214,7 @@ img { [margin-end grid-end]; --grid-total-width: 80rem; - --grid-max-content-width: 40rem; + --grid-max-content-width: 50rem; } } diff --git a/http/src/css/feed.uppcss b/http/src/css/feed.uppcss index ae7c05d..5db8431 100644 --- a/http/src/css/feed.uppcss +++ b/http/src/css/feed.uppcss @@ -15,6 +15,25 @@ ul:has(> .h-entry) { .h-feed .h-entry { display: flex; flex-direction: column; + + &:has(details) { + margin-inline: calc(-1 * var(--spacing-inline-lg)); + border: 2px solid; + padding-inline: var(--spacing-inline-lg); + padding-block: var(--spacing-block-sm); + background: var(--colour-primary-bg-accent); + cursor: pointer; + } + + > details > summary { + margin-inline-start: -1rem; + &::marker { + font-size: 1rem; + } + :is(h2, h3, h4, h5, h6) { + display: inline; + } + } } .h-feed .h-entry + .h-entry { @@ -26,6 +45,10 @@ ul:has(> .h-entry) { margin: 0; } +.h-feed .h-entry summary :is(h2, h3, h4, h5, h6) { + margin: 0; +} + .h-feed .h-entry .entry-meta { order: 0; font-size: var(--font-size-sm); @@ -38,6 +61,7 @@ ul:has(> .h-entry) { > * + *::before { content: '•'; padding-inline: var(--spacing-inline-sm); + } } .h-feed .h-entry .p-name { @@ -52,17 +76,3 @@ ul:has(> .h-entry) { .h-feed :is(a.full-feed-link, .full-feed-link a)::after { content: ' >'; } - -.h-feed .e-content { - border-bottom: solid 1px; - &:not(.e-content--expanded) { - height: 10rem; - overflow: clip; - } -} - -.h-feed .h-entry > button { - width: max-content; - align-self: center; - margin-block-start: var(--spacing-block-xs); -} |
