Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a21044738d | ||
|
|
929c06d957 | ||
|
|
198efddb54 | ||
|
|
39408dffe8 | ||
|
|
6961dc24e5 | ||
|
|
e8b9d70061 |
@@ -90,7 +90,7 @@ type ServerCommand struct {
|
||||
EnableEmoji bool `long:"emoji" env:"EMOJI" description:"enable emoji"`
|
||||
SimpleView bool `long:"simple-view" env:"SIMPLE_VIEW" description:"minimal comment editor mode"`
|
||||
ProxyCORS bool `long:"proxy-cors" env:"PROXY_CORS" description:"disable internal CORS and delegate it to proxy"`
|
||||
AllowedHosts []string `long:"allowed-hosts" env:"ALLOWED_HOSTS" description:"limit hosts/sources allowed to embed comments via CSP 'frame-ancestors''" env-delim:","`
|
||||
AllowedHosts []string `long:"allowed-hosts" env:"ALLOWED_HOSTS" description:"limit hosts/sources allowed to embed comments via CSP 'frame-ancestors'" env-delim:","`
|
||||
SubscribersOnly bool `long:"subscribers-only" env:"SUBSCRIBERS_ONLY" description:"enable commenting only for Patreon subscribers"`
|
||||
DisableSignature bool `long:"disable-signature" env:"DISABLE_SIGNATURE" description:"disable server signature in headers"`
|
||||
DisableFancyTextFormatting bool `long:"disable-fancy-text-formatting" env:"DISABLE_FANCY_TEXT_FORMATTING" description:"disable fancy comments text formatting (replacement of quotes, dashes, fractions, etc)"`
|
||||
@@ -123,10 +123,10 @@ type ServerCommand struct {
|
||||
Subject string `long:"subj" env:"SUBJ" default:"remark42 confirmation" description:"email's subject"`
|
||||
ContentType string `long:"content-type" env:"CONTENT_TYPE" default:"text/html" description:"content type"`
|
||||
Host string `long:"host" env:"HOST" description:"[deprecated, use --smtp.host] SMTP host"`
|
||||
Port int `long:"port" env:"PORT" description:"[deprecated, use --smtp.port] SMTP password"`
|
||||
SMTPPassword string `long:"passwd" env:"PASSWD" description:"[deprecated, use --smtp.password] SMTP port"`
|
||||
SMTPUserName string `long:"user" env:"USER" description:"[deprecated, use --smtp.username] enable TLS"`
|
||||
TLS bool `long:"tls" env:"TLS" description:"[deprecated, use --smtp.tls] SMTP TCP connection timeout"`
|
||||
Port int `long:"port" env:"PORT" description:"[deprecated, use --smtp.port] SMTP port"`
|
||||
SMTPPassword string `long:"passwd" env:"PASSWD" description:"[deprecated, use --smtp.password] SMTP password"`
|
||||
SMTPUserName string `long:"user" env:"USER" description:"[deprecated, use --smtp.username] SMTP user name"`
|
||||
TLS bool `long:"tls" env:"TLS" description:"[deprecated, use --smtp.tls] enable TLS"`
|
||||
TimeOut time.Duration `long:"timeout" env:"TIMEOUT" default:"10s" description:"[deprecated, use --smtp.timeout] SMTP TCP connection timeout"`
|
||||
MsgTemplate string `long:"template" env:"TEMPLATE" description:"[deprecated] message template file" default:"email_confirmation_login.html.tmpl"`
|
||||
} `group:"email" namespace:"email" env-namespace:"EMAIL"`
|
||||
@@ -283,8 +283,8 @@ type NotifyGroup struct {
|
||||
} `group:"slack" namespace:"slack" env-namespace:"SLACK"`
|
||||
Webhook struct {
|
||||
URL string `long:"url" env:"URL" description:"webhook URL for admin notifications"`
|
||||
Template string `long:"template" env:"TEMPLATE" description:"webhook authentication template" default:"{\"text\": \"{{.Text}}\"}"`
|
||||
Headers []string `long:"headers" description:"webhook authentication headers in format --notify.webhook.headers=Header1:Value1,Value2,... [$NOTIFY_WEBHOOK_HEADERS]"` // env NOTIFY_WEBHOOK_HEADERS split in code bellow to allow , inside ""
|
||||
Template string `long:"template" env:"TEMPLATE" description:"webhook payload template (Go text/template); falls back to {\"text\": {{.Text | escapeJSONString}}} when empty"`
|
||||
Headers []string `long:"headers" description:"webhook headers in format --notify.webhook.headers=Header1:Value1,Value2,... [$NOTIFY_WEBHOOK_HEADERS]"` // env NOTIFY_WEBHOOK_HEADERS split in code below to allow , inside ""
|
||||
Timeout time.Duration `long:"timeout" env:"TIMEOUT" description:"webhook timeout" default:"5s"`
|
||||
} `group:"webhook" namespace:"webhook" env-namespace:"WEBHOOK"`
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import { Auth } from 'components/auth';
|
||||
import { Avatar } from 'components/avatar';
|
||||
import { SignOutIcon } from 'components/icons/signout';
|
||||
import { IconButton } from 'components/icon-button/icon-button';
|
||||
import { messages } from 'components/auth/auth.messsages';
|
||||
import { messages } from 'components/auth/auth.messages';
|
||||
|
||||
import styles from './auth-panel.module.css';
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useIntl } from 'react-intl';
|
||||
import { errorMessages, RequestError } from 'utils/errorUtils';
|
||||
import { isObject } from 'utils/is-object';
|
||||
import { parseMessage, updateIframeHeight } from 'utils/post-message';
|
||||
import { messages } from './auth.messsages';
|
||||
import { messages } from './auth.messages';
|
||||
|
||||
export function useDropdown(disableClosing?: boolean) {
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@@ -14,7 +14,7 @@ import { ArrowIcon } from 'components/icons/arrow';
|
||||
|
||||
import { Button } from './components/button';
|
||||
import { OAuth } from './components/oauth';
|
||||
import { messages } from './auth.messsages';
|
||||
import { messages } from './auth.messages';
|
||||
import { useDropdown, useErrorMessage } from './auth.hooks';
|
||||
import { getProviders, getTokenInvalidReason, persistEmail } from './auth.utils';
|
||||
import {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { isJwtExpired } from 'utils/jwt';
|
||||
import { StaticStore } from 'common/static-store';
|
||||
import type { FormProvider, OAuthProvider } from 'common/types';
|
||||
|
||||
import { messages } from './auth.messsages';
|
||||
import { messages } from './auth.messages';
|
||||
import { setItem, getItem } from 'common/local-storage';
|
||||
import { LS_EMAIL_KEY } from 'common/constants';
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { OAuthProvider } from 'common/types';
|
||||
import { siteId } from 'common/settings';
|
||||
import { useTheme } from 'hooks/useTheme';
|
||||
|
||||
import { messages } from 'components/auth/auth.messsages';
|
||||
import { messages } from 'components/auth/auth.messages';
|
||||
|
||||
import { BASE_URL } from 'common/constants.config';
|
||||
import { getButtonVariant, getProviderData } from './oauth.utils';
|
||||
|
||||
@@ -18,12 +18,13 @@ import { Spinner } from 'components/spinner/spinner';
|
||||
import { CrossIcon } from 'components/icons/cross';
|
||||
import { IconButton } from 'components/icon-button/icon-button';
|
||||
import { Button } from 'components/auth/components/button';
|
||||
import { messages as authMessages } from 'components/auth/auth.messsages';
|
||||
import { messages } from 'components/auth/auth.messages';
|
||||
import type { Comment as CommentType, Theme, User } from 'common/types';
|
||||
|
||||
import styles from './profile.module.css';
|
||||
import { Counter } from './components/counter';
|
||||
|
||||
import styles from './profile.module.css';
|
||||
|
||||
const COMMENTS_LIMIT = 10;
|
||||
|
||||
async function signout() {
|
||||
@@ -198,7 +199,7 @@ export function Profile() {
|
||||
{isCurrent && (
|
||||
<button
|
||||
className={clsx('profile-signout', styles.signout)}
|
||||
title={intl.formatMessage(authMessages.signout)}
|
||||
title={intl.formatMessage(messages.signout)}
|
||||
onClick={handleClickLogout}
|
||||
disabled={isSigningOut}
|
||||
>
|
||||
|
||||
@@ -31,18 +31,12 @@
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.thread {
|
||||
&:last-child {
|
||||
margin-bottom: -24px;
|
||||
}
|
||||
}
|
||||
|
||||
.threads {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
margin: 48px 0 0;
|
||||
margin: 24px 0 0;
|
||||
font-size: 12px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@@ -301,9 +301,7 @@ function Comments({ isLoading, topComments, commentsShown, showMore }: CommentsP
|
||||
) : (
|
||||
<>
|
||||
{topComments.length > 0 &&
|
||||
renderComments.map((id) => (
|
||||
<Thread key={`thread-${id}`} id={id} mix={styles.thread} level={0} getPreview={getPreview} />
|
||||
))}
|
||||
renderComments.map((id) => <Thread key={`thread-${id}`} id={id} level={0} getPreview={getPreview} />)}
|
||||
{isShowMoreButtonVisible && (
|
||||
<Button className={styles.moreComments} onClick={showMore}>
|
||||
<FormattedMessage id="root.show-more" defaultMessage="Show more" />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import clsx from 'clsx';
|
||||
import { h } from 'preact';
|
||||
import { messages } from 'components/auth/auth.messsages';
|
||||
import { messages } from 'components/auth/auth.messages';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import styles from './spinner.module.css';
|
||||
|
||||
@@ -19,7 +19,6 @@ interface Props {
|
||||
id: CommentInterface['id'];
|
||||
childs?: CommentInterface['id'][];
|
||||
level: number;
|
||||
mix?: string;
|
||||
|
||||
getPreview(text: string): Promise<string>;
|
||||
}
|
||||
@@ -34,7 +33,7 @@ const commentSelector = (id: string) => (state: StoreState) => {
|
||||
return { comment, childs, collapsed, theme };
|
||||
};
|
||||
|
||||
export const Thread: FunctionComponent<Props> = ({ id, level, mix, getPreview }) => {
|
||||
export const Thread: FunctionComponent<Props> = ({ id, level, getPreview }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const intl = useIntl();
|
||||
const { collapsed, comment, childs, theme } = useAppSelector(commentSelector(id), shallowEqual);
|
||||
@@ -53,8 +52,7 @@ export const Thread: FunctionComponent<Props> = ({ id, level, mix, getPreview })
|
||||
styles.root,
|
||||
indented && styles.indented,
|
||||
level === 6 && styles.level6,
|
||||
theme === 'dark' && styles.themeDark,
|
||||
mix
|
||||
theme === 'dark' && styles.themeDark
|
||||
)}
|
||||
role={['listitem'].concat(!collapsed && !!repliesCount ? 'list' : []).join(' ')}
|
||||
aria-expanded={!collapsed}
|
||||
|
||||
@@ -8,6 +8,13 @@ body {
|
||||
color: rgb(var(--primary-text-color));
|
||||
background: transparent;
|
||||
box-sizing: border-box;
|
||||
|
||||
/* parent resizes the iframe element to fit content via postMessage, so the
|
||||
iframe document never needs its own scrollbar. spec-correct replacement
|
||||
for the deprecated `scrolling="no"` attribute removed in c26f45e5 — the
|
||||
`overflow:hidden` on the iframe ELEMENT doesn't control the iframe
|
||||
DOCUMENT's scrollbars, that has to be set inside the document. */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
input,
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
"markdown-it": "^14.0.0",
|
||||
"markdown-it-anchor": "^9.2.0",
|
||||
"markdown-it-container": "^4.0.0",
|
||||
"node-fetch": "^3.3.2",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.4.1",
|
||||
"tailwindcss": "^3.4.15"
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
const DEFAULT_DATA = { latestVersion: '' }
|
||||
let currentData = null
|
||||
|
||||
module.exports = async function getLatestReleaseVersion() {
|
||||
if (currentData) {
|
||||
return currentData
|
||||
}
|
||||
try {
|
||||
const fetch = (await import('node-fetch')).default;
|
||||
const res = await fetch(
|
||||
'https://api.github.com/repos/umputun/remark42/releases'
|
||||
)
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`[ERROR] Status: ${res.status}: ${res.statusText}`)
|
||||
}
|
||||
|
||||
const data = await res.json()
|
||||
|
||||
currentData = { latestVersion: data[0].tag_name || '' }
|
||||
|
||||
return currentData
|
||||
} catch (e) {
|
||||
console.error(e.message)
|
||||
return DEFAULT_DATA
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
"description": "Remark42 is a self-hosted, lightweight, and simple (yet functional) comment engine, which doesn't spy on users. It can be embedded into blogs, articles or any other place where readers add comments.",
|
||||
"url": "https://remark42.com",
|
||||
"githubUrl": "https://github.com/umputun/remark42",
|
||||
"githubApiUrl": "https://api.github.com/repos/umputun/remark42",
|
||||
"githubBranch": "master",
|
||||
"remark42Host": "https://demo.remark42.com"
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ services:
|
||||
| image.fs.path | IMAGE_FS_PATH | `./var/pictures` | permanent location of images |
|
||||
| image.fs.staging | IMAGE_FS_STAGING | `./var/pictures.staging` | staging location of images |
|
||||
| image.fs.partitions | IMAGE_FS_PARTITIONS | `100` | number of image partitions |
|
||||
| image.bolt.file | IMAGE_BOLT_FILE | `/var/pictures.db` | images bolt file location |
|
||||
| image.bolt.file | IMAGE_BOLT_FILE | `./var/pictures.db` | images bolt file location |
|
||||
| image.rpc.api | IMAGE_RPC_API | | rpc extension api url |
|
||||
| image.rpc.timeout | IMAGE_RPC_TIMEOUT | | http timeout (default: 5s) |
|
||||
| image.rpc.auth_user | IMAGE_RPC_AUTH_USER | | basic auth user name |
|
||||
@@ -109,9 +109,6 @@ services:
|
||||
| auth.custom.name-field | AUTH_CUSTOM_NAME_FIELD | `name` | user info field used as display name |
|
||||
| auth.custom.picture-field | AUTH_CUSTOM_PICTURE_FIELD | `picture` | user info field used as avatar URL |
|
||||
| auth.custom.email-field | AUTH_CUSTOM_EMAIL_FIELD | `email` | user info field used as email |
|
||||
|
||||
Custom OAuth2 integration currently supports only one custom provider at a time, and `AUTH_CUSTOM_NAME` must match `^[a-z0-9][a-z0-9_-]*$`.
|
||||
|
||||
| auth.telegram | AUTH_TELEGRAM | `false` | Enable Telegram auth (telegram.token must be present) |
|
||||
| auth.yandex.cid | AUTH_YANDEX_CID | | Yandex OAuth client ID |
|
||||
| auth.yandex.csec | AUTH_YANDEX_CSEC | | Yandex OAuth client secret |
|
||||
@@ -128,7 +125,7 @@ Custom OAuth2 integration currently supports only one custom provider at a time,
|
||||
| notify.slack.token | NOTIFY_SLACK_TOKEN | | Slack token |
|
||||
| notify.slack.chan | NOTIFY_SLACK_CHAN | `general` | Slack channel for admin notifications |
|
||||
| notify.webhook.url | NOTIFY_WEBHOOK_URL | | Webhook notification URL for admin notifications |
|
||||
| notify.webhook.template | NOTIFY_WEBHOOK_TEMPLATE | `{"text": {{.Text | escapeJSONString}}}` | Webhook payload template |
|
||||
| notify.webhook.template | NOTIFY_WEBHOOK_TEMPLATE | `{"text": {{.Text \| escapeJSONString}}}` | Webhook payload template (Go text/template) |
|
||||
| notify.webhook.headers | NOTIFY_WEBHOOK_HEADERS | | HTTP header in format Header1:Value1,Header2:Value2,... |
|
||||
| notify.webhook.timeout | NOTIFY_WEBHOOK_TIMEOUT | `5s` | Webhook connection timeout |
|
||||
| notify.email.from_address | NOTIFY_EMAIL_FROM | | from email address (e.g. `john.doe@example.com` or `"John Doe"<john.doe@example.com>`) |
|
||||
@@ -139,7 +136,7 @@ Custom OAuth2 integration currently supports only one custom provider at a time,
|
||||
| smtp.port | SMTP_PORT | | SMTP port |
|
||||
| smtp.username | SMTP_USERNAME | | SMTP user name |
|
||||
| smtp.password | SMTP_PASSWORD | | SMTP password |
|
||||
| smtp.login_auth | SMTP_LOGIN_AUTH | `false | enable LOGIN auth instead of PLAIN |
|
||||
| smtp.login_auth | SMTP_LOGIN_AUTH | `false` | enable LOGIN auth instead of PLAIN |
|
||||
| smtp.tls | SMTP_TLS | `false` | enable TLS for SMTP |
|
||||
| smtp.starttls | SMTP_STARTTLS | `false` | enable StartTLS for SMTP |
|
||||
| smtp.insecure_skip_verify | SMTP_INSECURE_SKIP_VERIFY | `false` | skip certificate verification for SMTP |
|
||||
@@ -184,6 +181,10 @@ Custom OAuth2 integration currently supports only one custom provider at a time,
|
||||
- _multi_ parameters separated by `,` in the environment or repeated with command-line keys, like `--site=s1 --site=s2 ...`
|
||||
- _required_ parameters have to be presented in the environment or provided in the command-line
|
||||
|
||||
### Custom OAuth2 integration
|
||||
|
||||
Custom OAuth2 integration currently supports only one custom provider at a time, and `AUTH_CUSTOM_NAME` must match `^[a-z0-9][a-z0-9_-]*$`.
|
||||
|
||||
### Security Considerations for auth.send-jwt-header
|
||||
|
||||
When `auth.send-jwt-header=true` is enabled:
|
||||
|
||||
@@ -33,7 +33,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: remark42
|
||||
image: ghcr.io/umputun/remark42:v1.14.0
|
||||
image: ghcr.io/umputun/remark42:v1.16.0
|
||||
ports:
|
||||
# http:
|
||||
- containerPort: 8080
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
{% endif %}
|
||||
<meta name="description" content="{{ metaDescription or renderData.metaDescription or site.description }}">
|
||||
<link rel="stylesheet" href="/style.css?v={% version %}"/>
|
||||
<script>{% include "inline.js" %}</script>
|
||||
<script>{% include "inline.njk" %}</script>
|
||||
{% set js %}
|
||||
{% include "script.js" %}
|
||||
{% endset %}
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
<a class="inline-block leading-5 font-medium border-b dark:border-gray-600 transition hover:text-brand-600 dark:hover:text-brand-300 hover:border-brand-300 dark:hover:border-brand-500" href="/docs/getting-started/installation/">Docs</a>
|
||||
</nav>
|
||||
<div class="order-2 md:order-3 flex space-x-2 ml-auto py-1 px-4">
|
||||
<a class="hover:text-gray-800 dark:hover:text-gray-300 flex items-center" aria-label="Remark42's GitHub Repository" rel="noopener noreferrer" href="{{ site.githubUrl}}/releases" target="_blank">
|
||||
<span class="mr-2">{{ github.latestVersion }}</span>
|
||||
<a class="hover:text-gray-800 dark:hover:text-gray-300 flex items-center" aria-label="{{ site.name }} releases on GitHub" rel="noopener noreferrer" href="{{ site.githubUrl }}/releases" target="_blank">
|
||||
<span class="mr-2" data-remark42-version hidden></span>
|
||||
<svg width="28" height="28" viewBox="0 0 24 24" fill="none">
|
||||
<path fill="currentColor" d="M12 3C7.0275 3 3 7.12937 3 12.2276C3 16.3109 5.57625 19.7597 9.15374 20.9824C9.60374 21.0631 9.77249 20.7863 9.77249 20.5441C9.77249 20.3249 9.76125 19.5982 9.76125 18.8254C7.5 19.2522 6.915 18.2602 6.735 17.7412C6.63375 17.4759 6.19499 16.6569 5.8125 16.4378C5.4975 16.2647 5.0475 15.838 5.80124 15.8264C6.51 15.8149 7.01625 16.4954 7.18499 16.7723C7.99499 18.1679 9.28875 17.7758 9.80625 17.5335C9.885 16.9337 10.1212 16.53 10.38 16.2993C8.3775 16.0687 6.285 15.2728 6.285 11.7432C6.285 10.7397 6.63375 9.9092 7.20749 9.26326C7.1175 9.03257 6.8025 8.08674 7.2975 6.81794C7.2975 6.81794 8.05125 6.57571 9.77249 7.76377C10.4925 7.55615 11.2575 7.45234 12.0225 7.45234C12.7875 7.45234 13.5525 7.55615 14.2725 7.76377C15.9937 6.56418 16.7475 6.81794 16.7475 6.81794C17.2424 8.08674 16.9275 9.03257 16.8375 9.26326C17.4113 9.9092 17.76 10.7281 17.76 11.7432C17.76 15.2843 15.6563 16.0687 13.6537 16.2993C13.98 16.5877 14.2613 17.1414 14.2613 18.0065C14.2613 19.2407 14.25 20.2326 14.25 20.5441C14.25 20.7863 14.4188 21.0746 14.8688 20.9824C16.6554 20.364 18.2079 19.1866 19.3078 17.6162C20.4077 16.0457 20.9995 14.1611 21 12.2276C21 7.12937 16.9725 3 12 3Z"></path>
|
||||
</svg>
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
const mq = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
const theme = localStorage.getItem('theme')
|
||||
|
||||
if ((theme && theme === 'dark') || (!theme && mq.matches)) {
|
||||
document.documentElement.classList.add('dark')
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
const mq = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
const theme = localStorage.getItem('theme')
|
||||
|
||||
if ((theme && theme === 'dark') || (!theme && mq.matches)) {
|
||||
document.documentElement.classList.add('dark')
|
||||
}
|
||||
|
||||
// fetch the latest release version client-side so the badge tracks releases
|
||||
// without needing a site rebuild. cached in sessionStorage for up to 1h so
|
||||
// repeated navigations within a tab don't re-hit the GitHub API on every page
|
||||
// load. placeholder is `hidden` in the template so a failed/blocked fetch
|
||||
// leaves no stray gap.
|
||||
const versionCacheKey = 'remark42-latest-version'
|
||||
const versionCacheTTL = 60 * 60 * 1000
|
||||
|
||||
// script is loaded synchronously in <head>, so a cache-hit fetch can resolve
|
||||
// before <body> is parsed and [data-remark42-version] exists. defer the DOM
|
||||
// update until the document is ready.
|
||||
function applyVersion(tag) {
|
||||
const update = () => {
|
||||
document.querySelectorAll('[data-remark42-version]').forEach((el) => {
|
||||
el.textContent = tag
|
||||
el.hidden = false
|
||||
})
|
||||
}
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', update)
|
||||
} else {
|
||||
update()
|
||||
}
|
||||
}
|
||||
|
||||
function readVersionCache() {
|
||||
try {
|
||||
const raw = sessionStorage.getItem(versionCacheKey)
|
||||
if (!raw) return null
|
||||
const { tag, expires } = JSON.parse(raw)
|
||||
if (!tag || typeof expires !== 'number' || Date.now() > expires) return null
|
||||
return tag
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function writeVersionCache(tag) {
|
||||
try {
|
||||
sessionStorage.setItem(versionCacheKey, JSON.stringify({ tag, expires: Date.now() + versionCacheTTL }))
|
||||
} catch (e) {
|
||||
// sessionStorage unavailable or full — fall through, fetch will run next load
|
||||
}
|
||||
}
|
||||
|
||||
const cachedVersion = readVersionCache()
|
||||
if (cachedVersion) {
|
||||
applyVersion(cachedVersion)
|
||||
} else {
|
||||
fetch('{{ site.githubApiUrl }}/releases/latest')
|
||||
.then((r) => (r.ok ? r.json() : Promise.reject(new Error('HTTP ' + r.status))))
|
||||
.then((d) => {
|
||||
if (!d || !d.tag_name) return
|
||||
writeVersionCache(d.tag_name)
|
||||
applyVersion(d.tag_name)
|
||||
})
|
||||
.catch((err) => console.warn('remark42-site: latest version fetch failed', err))
|
||||
}
|
||||
@@ -598,11 +598,6 @@ cssesc@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
|
||||
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
|
||||
|
||||
data-uri-to-buffer@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e"
|
||||
integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==
|
||||
|
||||
data-view-buffer@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2"
|
||||
@@ -922,14 +917,6 @@ fastq@^1.6.0:
|
||||
dependencies:
|
||||
reusify "^1.0.4"
|
||||
|
||||
fetch-blob@^3.1.2, fetch-blob@^3.1.4:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9"
|
||||
integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==
|
||||
dependencies:
|
||||
node-domexception "^1.0.0"
|
||||
web-streams-polyfill "^3.0.3"
|
||||
|
||||
filelist@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5"
|
||||
@@ -972,13 +959,6 @@ foreground-child@^3.1.0:
|
||||
cross-spawn "^7.0.0"
|
||||
signal-exit "^4.0.1"
|
||||
|
||||
formdata-polyfill@^4.0.10:
|
||||
version "4.0.10"
|
||||
resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423"
|
||||
integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==
|
||||
dependencies:
|
||||
fetch-blob "^3.1.2"
|
||||
|
||||
fs.realpath@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||
@@ -1770,20 +1750,6 @@ no-case@^2.2.0:
|
||||
dependencies:
|
||||
lower-case "^1.1.1"
|
||||
|
||||
node-domexception@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5"
|
||||
integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==
|
||||
|
||||
node-fetch@^3.3.2:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b"
|
||||
integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==
|
||||
dependencies:
|
||||
data-uri-to-buffer "^4.0.0"
|
||||
fetch-blob "^3.1.4"
|
||||
formdata-polyfill "^4.0.10"
|
||||
|
||||
normalize-package-data@^2.3.2:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
|
||||
@@ -2750,11 +2716,6 @@ void-elements@^3.1.0:
|
||||
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09"
|
||||
integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==
|
||||
|
||||
web-streams-polyfill@^3.0.3:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b"
|
||||
integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==
|
||||
|
||||
which-boxed-primitive@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
|
||||
|
||||
Reference in New Issue
Block a user