mirror of
https://github.com/Athou/commafeed.git
synced 2026-03-21 21:37:29 +00:00
Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74bce1308c | ||
|
|
98cfa6d2c8 | ||
|
|
99a02a2186 | ||
|
|
3431a813b1 | ||
|
|
e9e0e8d32b | ||
|
|
2d14409d35 | ||
|
|
a8200e5c58 | ||
|
|
79a8df8b06 | ||
|
|
061a5f0262 | ||
|
|
821bdb3b0f | ||
|
|
606dfa9299 | ||
|
|
131357c616 | ||
|
|
f6d3493bad | ||
|
|
0c6104e25b | ||
|
|
d73735a35d | ||
|
|
e725d2d6b6 | ||
|
|
f0e1279d68 | ||
|
|
c74c74d2c4 | ||
|
|
aa70cf5dcd | ||
|
|
1055259627 | ||
|
|
302d37b6ef | ||
|
|
8532a73d94 | ||
|
|
ffafb272cb | ||
|
|
22e0171a34 | ||
|
|
2b410f040c | ||
|
|
259e8ad4e5 | ||
|
|
21244dd9f5 | ||
|
|
bc6206180d | ||
|
|
6e22d21358 | ||
|
|
95bdb4e700 | ||
|
|
9b7dbc68ab | ||
|
|
dc86c9b0db | ||
|
|
cb92ed753f | ||
|
|
10a085e24e | ||
|
|
3400a39edf | ||
|
|
21efffa345 | ||
|
|
e2e80ba7e5 | ||
|
|
d988dba66e | ||
|
|
403201fbff | ||
|
|
3cc93b51bb | ||
|
|
6a7d83bb45 | ||
|
|
19c8db8b31 | ||
|
|
0d75688ec8 | ||
|
|
e01dcb2f5b | ||
|
|
57757e2c14 | ||
|
|
779cd2fcfe | ||
|
|
94919f22e4 |
@@ -1,5 +1,14 @@
|
||||
# Changelog
|
||||
|
||||
## [5.3.1]
|
||||
|
||||
- Fixed an issue that could cause some HTTP feeds to return a 400 error (#1572)
|
||||
|
||||
## [5.3.0]
|
||||
|
||||
- Added a setting to set a cooldown on the "fetch all my feeds" action, disabled by default (#1556)
|
||||
- Fixed an issue that could cause entries to not correctly load when using the "next" header button (#1557)
|
||||
|
||||
## [5.2.0]
|
||||
|
||||
- Added an option to keep a number of entries above the selected entry when scrolling
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/1.9.1/schema.json",
|
||||
"$schema": "https://biomejs.dev/schemas/1.9.3/schema.json",
|
||||
"formatter": {
|
||||
"indentStyle": "space",
|
||||
"indentWidth": 4,
|
||||
|
||||
607
commafeed-client/package-lock.json
generated
607
commafeed-client/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -20,19 +20,19 @@
|
||||
"@lingui/core": "^4.11.4",
|
||||
"@lingui/macro": "^4.11.4",
|
||||
"@lingui/react": "^4.11.4",
|
||||
"@mantine/core": "^7.12.2",
|
||||
"@mantine/form": "^7.12.2",
|
||||
"@mantine/hooks": "^7.12.2",
|
||||
"@mantine/modals": "^7.12.2",
|
||||
"@mantine/notifications": "^7.12.2",
|
||||
"@mantine/spotlight": "^7.12.2",
|
||||
"@mantine/core": "^7.13.2",
|
||||
"@mantine/form": "^7.13.2",
|
||||
"@mantine/hooks": "^7.13.2",
|
||||
"@mantine/modals": "^7.13.2",
|
||||
"@mantine/notifications": "^7.13.2",
|
||||
"@mantine/spotlight": "^7.13.2",
|
||||
"@monaco-editor/react": "^4.6.0",
|
||||
"@reduxjs/toolkit": "^2.2.7",
|
||||
"axios": "^1.7.7",
|
||||
"dayjs": "^1.11.13",
|
||||
"escape-string-regexp": "^5.0.0",
|
||||
"interweave": "^13.1.0",
|
||||
"monaco-editor": "^0.51.0",
|
||||
"monaco-editor": "^0.52.0",
|
||||
"mousetrap": "^1.6.5",
|
||||
"react": "^18.3.1",
|
||||
"react-async-hook": "^4.0.0",
|
||||
@@ -54,26 +54,26 @@
|
||||
"websocket-heartbeat-js": "^1.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^1.9.1",
|
||||
"@biomejs/biome": "^1.9.3",
|
||||
"@lingui/cli": "^4.11.4",
|
||||
"@lingui/vite-plugin": "^4.11.4",
|
||||
"@types/mousetrap": "^1.6.15",
|
||||
"@types/react": "^18.3.7",
|
||||
"@types/react": "^18.3.11",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/react-helmet": "^6.1.11",
|
||||
"@types/react-infinite-scroller": "^1.2.5",
|
||||
"@types/swagger-ui-react": "^4.18.3",
|
||||
"@types/throttle-debounce": "^5.0.2",
|
||||
"@types/tinycon": "^0.6.5",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"@vitejs/plugin-react": "^4.3.2",
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"jsdom": "^25.0.0",
|
||||
"jsdom": "^25.0.1",
|
||||
"rollup-plugin-visualizer": "^5.12.0",
|
||||
"typescript": "^5.6.2",
|
||||
"vite": "^5.4.6",
|
||||
"vite": "^5.4.8",
|
||||
"vite-plugin-checker": "^0.8.0",
|
||||
"vite-tsconfig-paths": "^5.0.1",
|
||||
"vitest": "^2.1.1",
|
||||
"vitest": "^2.1.2",
|
||||
"vitest-mock-extended": "^2.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,16 +6,16 @@
|
||||
<parent>
|
||||
<groupId>com.commafeed</groupId>
|
||||
<artifactId>commafeed</artifactId>
|
||||
<version>5.2.0</version>
|
||||
<version>5.3.1</version>
|
||||
</parent>
|
||||
<artifactId>commafeed-client</artifactId>
|
||||
<name>CommaFeed Client</name>
|
||||
|
||||
<properties>
|
||||
<!-- renovate: datasource=node-version depName=node -->
|
||||
<node.version>v20.17.0</node.version>
|
||||
<node.version>v20.18.0</node.version>
|
||||
<!-- renovate: datasource=npm depName=npm -->
|
||||
<npm.version>10.8.3</npm.version>
|
||||
<npm.version>10.9.0</npm.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
@@ -23,7 +23,7 @@
|
||||
<plugin>
|
||||
<groupId>com.github.eirslett</groupId>
|
||||
<artifactId>frontend-maven-plugin</artifactId>
|
||||
<version>1.15.0</version>
|
||||
<version>1.15.1</version>
|
||||
<?m2e ignore?>
|
||||
<executions>
|
||||
<execution>
|
||||
|
||||
@@ -230,7 +230,7 @@ export const selectPreviousEntry = createAppAsyncThunk(
|
||||
)
|
||||
export const selectNextEntry = createAppAsyncThunk(
|
||||
"entries/entry/selectNext",
|
||||
(
|
||||
async (
|
||||
arg: {
|
||||
expand: boolean
|
||||
markAsRead: boolean
|
||||
@@ -239,12 +239,20 @@ export const selectNextEntry = createAppAsyncThunk(
|
||||
thunkApi
|
||||
) => {
|
||||
const state = thunkApi.getState()
|
||||
const { entries } = state.entries
|
||||
const { entries, hasMore, loading } = state.entries
|
||||
const nextIndex = entries.findIndex(e => e.id === state.entries.selectedEntryId) + 1
|
||||
if (nextIndex < entries.length) {
|
||||
|
||||
// load more entries if needed
|
||||
// this can happen if the last entry is too large to fit on the screen and the infinite loader doesn't trigger
|
||||
if (nextIndex >= entries.length && hasMore && !loading) {
|
||||
await thunkApi.dispatch(loadMoreEntries())
|
||||
}
|
||||
|
||||
const entriesAfterLoading = thunkApi.getState().entries.entries
|
||||
if (nextIndex < entriesAfterLoading.length) {
|
||||
thunkApi.dispatch(
|
||||
selectEntry({
|
||||
entry: entries[nextIndex],
|
||||
entry: entriesAfterLoading[nextIndex],
|
||||
expand: arg.expand,
|
||||
markAsRead: arg.markAsRead,
|
||||
scrollToEntry: arg.scrollToEntry,
|
||||
|
||||
@@ -220,6 +220,7 @@ export interface ServerInfo {
|
||||
websocketEnabled: boolean
|
||||
websocketPingInterval: number
|
||||
treeReloadInterval: number
|
||||
forceRefreshCooldownDuration: number
|
||||
}
|
||||
|
||||
export interface SharingSettings {
|
||||
@@ -287,6 +288,7 @@ export interface UserModel {
|
||||
created: number
|
||||
lastLogin?: number
|
||||
admin: boolean
|
||||
lastForceRefresh?: number
|
||||
}
|
||||
|
||||
export interface AdminSaveUserRequest {
|
||||
|
||||
@@ -2,14 +2,10 @@ import { Trans } from "@lingui/macro"
|
||||
import { Tooltip } from "@mantine/core"
|
||||
import { Constants } from "app/constants"
|
||||
import dayjs from "dayjs"
|
||||
import { useEffect, useState } from "react"
|
||||
import { useNow } from "hooks/useNow"
|
||||
|
||||
export function RelativeDate(props: { date: Date | number | undefined }) {
|
||||
const [now, setNow] = useState(new Date())
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => setNow(new Date()), 60 * 1000)
|
||||
return () => clearInterval(interval)
|
||||
}, [])
|
||||
const now = useNow(60 * 1000)
|
||||
|
||||
if (!props.date) return <Trans>N/A</Trans>
|
||||
const date = dayjs(props.date)
|
||||
|
||||
@@ -128,36 +128,28 @@ export function FeedEntries() {
|
||||
}, [dispatch, entries, viewMode, scrollMarks, scrollingToEntry])
|
||||
|
||||
useMousetrap("r", async () => await dispatch(reloadEntries()))
|
||||
useMousetrap("j", async () => {
|
||||
// load more entries if needed
|
||||
// this can happen if the last entry is too large to fit on the screen and the infinite loader doesn't trigger
|
||||
if (hasMore && !loading && selectedEntry === entries[entries.length - 1]) {
|
||||
await dispatch(loadMoreEntries())
|
||||
}
|
||||
|
||||
await dispatch(
|
||||
selectNextEntry({
|
||||
expand: true,
|
||||
markAsRead: true,
|
||||
scrollToEntry: true,
|
||||
})
|
||||
)
|
||||
})
|
||||
useMousetrap("n", async () => {
|
||||
// load more entries if needed
|
||||
// this can happen if the last entry is too large to fit on the screen and the infinite loader doesn't trigger
|
||||
if (hasMore && !loading && selectedEntry === entries[entries.length - 1]) {
|
||||
await dispatch(loadMoreEntries())
|
||||
}
|
||||
|
||||
await dispatch(
|
||||
selectNextEntry({
|
||||
expand: false,
|
||||
markAsRead: false,
|
||||
scrollToEntry: true,
|
||||
})
|
||||
)
|
||||
})
|
||||
useMousetrap(
|
||||
"j",
|
||||
async () =>
|
||||
await dispatch(
|
||||
selectNextEntry({
|
||||
expand: true,
|
||||
markAsRead: true,
|
||||
scrollToEntry: true,
|
||||
})
|
||||
)
|
||||
)
|
||||
useMousetrap(
|
||||
"n",
|
||||
async () =>
|
||||
await dispatch(
|
||||
selectNextEntry({
|
||||
expand: false,
|
||||
markAsRead: false,
|
||||
scrollToEntry: true,
|
||||
})
|
||||
)
|
||||
)
|
||||
useMousetrap(
|
||||
"k",
|
||||
async () =>
|
||||
|
||||
@@ -15,6 +15,9 @@ import { redirectToAbout, redirectToAdminUsers, redirectToDonate, redirectToMetr
|
||||
import { useAppDispatch, useAppSelector } from "app/store"
|
||||
import type { ViewMode } from "app/types"
|
||||
import { setViewMode } from "app/user/slice"
|
||||
import { reloadProfile } from "app/user/thunks"
|
||||
import dayjs from "dayjs"
|
||||
import { useNow } from "hooks/useNow"
|
||||
import { type ReactNode, useState } from "react"
|
||||
import {
|
||||
TbChartLine,
|
||||
@@ -92,12 +95,19 @@ const viewModeData: ViewModeControlItem[] = [
|
||||
|
||||
export function ProfileMenu(props: ProfileMenuProps) {
|
||||
const [opened, setOpened] = useState(false)
|
||||
const now = useNow()
|
||||
const profile = useAppSelector(state => state.user.profile)
|
||||
const admin = useAppSelector(state => state.user.profile?.admin)
|
||||
const viewMode = useAppSelector(state => state.user.localSettings.viewMode)
|
||||
const forceRefreshCooldownDuration = useAppSelector(state => state.server.serverInfos?.forceRefreshCooldownDuration)
|
||||
const dispatch = useAppDispatch()
|
||||
const { colorScheme, setColorScheme } = useMantineColorScheme()
|
||||
|
||||
const nextAvailableForceRefresh = profile?.lastForceRefresh
|
||||
? profile.lastForceRefresh + (forceRefreshCooldownDuration ?? 0)
|
||||
: now.getTime()
|
||||
const forceRefreshEnabled = nextAvailableForceRefresh <= now.getTime()
|
||||
|
||||
const logout = () => {
|
||||
window.location.href = "logout"
|
||||
}
|
||||
@@ -118,18 +128,32 @@ export function ProfileMenu(props: ProfileMenuProps) {
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
leftSection={<TbWorldDownload size={iconSize} />}
|
||||
onClick={async () =>
|
||||
await client.feed.refreshAll().then(() => {
|
||||
disabled={!forceRefreshEnabled}
|
||||
onClick={async () => {
|
||||
setOpened(false)
|
||||
|
||||
try {
|
||||
await client.feed.refreshAll()
|
||||
|
||||
// reload profile to update last force refresh timestamp
|
||||
await dispatch(reloadProfile())
|
||||
|
||||
showNotification({
|
||||
message: <Trans>Your feeds have been queued for refresh.</Trans>,
|
||||
color: "green",
|
||||
autoClose: 1000,
|
||||
})
|
||||
setOpened(false)
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
showNotification({
|
||||
message: <Trans>Force fetching feeds is not yet available.</Trans>,
|
||||
color: "red",
|
||||
autoClose: 2000,
|
||||
})
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Trans>Fetch all my feeds now</Trans>
|
||||
{!forceRefreshEnabled && <span> ({dayjs.duration(nextAvailableForceRefresh - now.getTime()).format("HH:mm:ss")})</span>}
|
||||
</Menu.Item>
|
||||
|
||||
<Divider />
|
||||
|
||||
10
commafeed-client/src/hooks/useNow.ts
Normal file
10
commafeed-client/src/hooks/useNow.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { useEffect, useState } from "react"
|
||||
|
||||
export const useNow = (interval = 1000): Date => {
|
||||
const [time, setTime] = useState(new Date())
|
||||
useEffect(() => {
|
||||
const t = setInterval(() => setTime(new Date()), interval)
|
||||
return () => clearInterval(t)
|
||||
}, [interval])
|
||||
return time
|
||||
}
|
||||
@@ -376,6 +376,10 @@ msgstr ""
|
||||
msgid "Filtering expression"
|
||||
msgstr "تصفية التعبير"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "هل نسيت كلمة المرور؟"
|
||||
|
||||
@@ -376,6 +376,10 @@ msgstr ""
|
||||
msgid "Filtering expression"
|
||||
msgstr "Expressió de filtratge"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "Heu oblidat la contrasenya?"
|
||||
|
||||
@@ -376,6 +376,10 @@ msgstr ""
|
||||
msgid "Filtering expression"
|
||||
msgstr "Filtrování výrazu"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "Zapomněli jste heslo?"
|
||||
|
||||
@@ -376,6 +376,10 @@ msgstr ""
|
||||
msgid "Filtering expression"
|
||||
msgstr "Hidlo mynegiant"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "Wedi anghofio cyfrinair?"
|
||||
|
||||
@@ -376,6 +376,10 @@ msgstr ""
|
||||
msgid "Filtering expression"
|
||||
msgstr "Filtrerende udtryk"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "Glemt adgangskode?"
|
||||
|
||||
@@ -376,6 +376,10 @@ msgstr ""
|
||||
msgid "Filtering expression"
|
||||
msgstr "Filterausdruck"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "Passwort vergessen?"
|
||||
|
||||
@@ -376,6 +376,10 @@ msgstr "Fever API URL"
|
||||
msgid "Filtering expression"
|
||||
msgstr "Filtering expression"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr "Force fetching feeds is not yet available."
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "Forgot password?"
|
||||
|
||||
@@ -377,6 +377,10 @@ msgstr "URL de la API de Fever"
|
||||
msgid "Filtering expression"
|
||||
msgstr "Expresión de filtrado"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "¿Olvidaste la contraseña?"
|
||||
|
||||
@@ -376,6 +376,10 @@ msgstr ""
|
||||
msgid "Filtering expression"
|
||||
msgstr "بیان فیلتر"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "رمز عبور را فراموش کرده اید؟"
|
||||
|
||||
@@ -376,6 +376,10 @@ msgstr ""
|
||||
msgid "Filtering expression"
|
||||
msgstr "Suodattava lauseke"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "Unohditko salasanan?"
|
||||
|
||||
@@ -323,7 +323,7 @@ msgstr "Entrez votre mot de passe actuel pour changer les paramètres du profil"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Entries to keep above the selected entry when scrolling"
|
||||
msgstr ""
|
||||
msgstr "Nombre d'entrées à conserver au-dessus de l'entrée sélectionnée lors d'un défilement"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Entry headers"
|
||||
@@ -376,6 +376,10 @@ msgstr "URL API Fever"
|
||||
msgid "Filtering expression"
|
||||
msgstr "Expression de filtrage"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr "La récupération forcée des flux n'est pas encore disponible."
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "Mot de passe oublié ?"
|
||||
@@ -614,7 +618,7 @@ msgstr "Sur mobile, afficher les boutons d'action en bas de l'écran"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Only applies to compact, cozy and detailed modes"
|
||||
msgstr ""
|
||||
msgstr "Ne fonctionne que dans les modes Compact, Cozy, et Vue détaillée"
|
||||
|
||||
#: src/pages/ErrorPage.tsx
|
||||
msgid "Oops!"
|
||||
|
||||
@@ -376,6 +376,10 @@ msgstr ""
|
||||
msgid "Filtering expression"
|
||||
msgstr "Expresión de filtrado"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "Esqueceches o contrasinal?"
|
||||
|
||||
@@ -376,6 +376,10 @@ msgstr ""
|
||||
msgid "Filtering expression"
|
||||
msgstr "Szűrő kifejezés"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "Elfelejtette a jelszavát?"
|
||||
|
||||
@@ -376,6 +376,10 @@ msgstr ""
|
||||
msgid "Filtering expression"
|
||||
msgstr "Memfilter ekspresi"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "Lupa kata sandi?"
|
||||
|
||||
@@ -376,6 +376,10 @@ msgstr ""
|
||||
msgid "Filtering expression"
|
||||
msgstr "Espressione filtrante"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "Password dimenticata?"
|
||||
|
||||
@@ -58,7 +58,7 @@ msgstr "ユーザー追加"
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
#: src/pages/admin/AdminUsersPage.tsx
|
||||
msgid "Admin"
|
||||
msgstr "管理人"
|
||||
msgstr "管理者"
|
||||
|
||||
#: src/app/constants.ts
|
||||
#: src/components/content/add/CategorySelect.tsx
|
||||
@@ -323,7 +323,7 @@ msgstr "プロファイル設定を変更するには、現在のパスワード
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Entries to keep above the selected entry when scrolling"
|
||||
msgstr ""
|
||||
msgstr "エントリーを選択したとき、読みやすさに応じたスクロール調整を行います。"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Entry headers"
|
||||
@@ -376,6 +376,10 @@ msgstr "Fever API URL"
|
||||
msgid "Filtering expression"
|
||||
msgstr "フィルタリング式"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr "フィードの強制フェッチはまだ利用できません。"
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "パスワードをお忘れですか?"
|
||||
@@ -398,7 +402,7 @@ msgstr "生成されたフィードURL"
|
||||
|
||||
#: src/components/content/FeedEntryContextMenu.tsx
|
||||
msgid "Go to {0}"
|
||||
msgstr "Go to {0}"
|
||||
msgstr "{0} に移動"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Go to the All view"
|
||||
@@ -418,11 +422,11 @@ msgstr "ID"
|
||||
|
||||
#: src/pages/app/FeedDetailsPage.tsx
|
||||
msgid "If not empty, an expression evaluating to 'true' or 'false'. If false, new entries for this feed will be marked as read automatically."
|
||||
msgstr "空でない場合は、'true' または 'false' に評価される式。 'false' の場合、このフィードの新しいエントリは自動的に既読としてマークされます。"
|
||||
msgstr "空でない場合は、'true' または 'false' に評価される式。 'false' の場合、このフィードの新しいエントリーは自動的に既読としてマークされます。"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "If the entry doesn't entirely fit on the screen"
|
||||
msgstr "エントリが画面に完全に収まらない場合"
|
||||
msgstr "エントリーが画面に完全に収まらない場合"
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "If you encounter an issue, please report it on the issues page of the GitHub project."
|
||||
@@ -614,7 +618,7 @@ msgstr "モバイルでは、画面の下部にアクションボタンを表示
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Only applies to compact, cozy and detailed modes"
|
||||
msgstr ""
|
||||
msgstr "これはコンパクト/cozy/詳細モードでのみ適用されます"
|
||||
|
||||
#: src/pages/ErrorPage.tsx
|
||||
msgid "Oops!"
|
||||
@@ -751,7 +755,7 @@ msgstr "保存"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Scroll selected entry to the top of the page"
|
||||
msgstr "選択されたエントリーをページの上部にスクロールする"
|
||||
msgstr "エントリーを選択したときのスクロール調整"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Scroll smoothly when navigating between entries"
|
||||
@@ -774,11 +778,11 @@ msgstr "検索には少なくとも3文字が必要です"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Set focus on next entry without opening it"
|
||||
msgstr "次のエントリーを開かずにフォーカスを設定する"
|
||||
msgstr "次のエントリーを開かずにフォーカスする"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Set focus on previous entry without opening it"
|
||||
msgstr "前のエントリーを開かずにフォーカスを設定する"
|
||||
msgstr "前のエントリーを開かずにフォーカスする"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Settings"
|
||||
|
||||
@@ -376,6 +376,10 @@ msgstr ""
|
||||
msgid "Filtering expression"
|
||||
msgstr "필터링 표현식"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "비밀번호를 잊으셨나요?"
|
||||
|
||||
@@ -376,6 +376,10 @@ msgstr ""
|
||||
msgid "Filtering expression"
|
||||
msgstr "Ungkapan penapisan"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "Lupa kata laluan?"
|
||||
|
||||
@@ -376,6 +376,10 @@ msgstr ""
|
||||
msgid "Filtering expression"
|
||||
msgstr "Filtrerende uttrykk"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "Glemt passord?"
|
||||
|
||||
@@ -376,6 +376,10 @@ msgstr ""
|
||||
msgid "Filtering expression"
|
||||
msgstr "Uitdrukking filteren"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "Wachtwoord vergeten?"
|
||||
|
||||
@@ -376,6 +376,10 @@ msgstr ""
|
||||
msgid "Filtering expression"
|
||||
msgstr "Filtrerende uttrykk"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "Glemt passord?"
|
||||
|
||||
@@ -376,6 +376,10 @@ msgstr ""
|
||||
msgid "Filtering expression"
|
||||
msgstr "Wyrażenie filtrujące"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "Zapomniałeś hasła?"
|
||||
|
||||
@@ -376,6 +376,10 @@ msgstr ""
|
||||
msgid "Filtering expression"
|
||||
msgstr "Filtrando expressão"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "Esqueceu a senha?"
|
||||
|
||||
@@ -376,6 +376,10 @@ msgstr "Ссылка Fever API"
|
||||
msgid "Filtering expression"
|
||||
msgstr "Выражение фильтрации"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "Забыли пароль?"
|
||||
|
||||
@@ -376,6 +376,10 @@ msgstr ""
|
||||
msgid "Filtering expression"
|
||||
msgstr "Filtrovanie výrazu"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "Zabudli ste heslo?"
|
||||
|
||||
@@ -376,6 +376,10 @@ msgstr ""
|
||||
msgid "Filtering expression"
|
||||
msgstr "Filtrerande uttryck"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "Glömt lösenord?"
|
||||
|
||||
@@ -376,6 +376,10 @@ msgstr ""
|
||||
msgid "Filtering expression"
|
||||
msgstr "Filtreleme ifadesi"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "Parolanızı mı unuttunuz?"
|
||||
|
||||
@@ -376,6 +376,10 @@ msgstr "Fever API 网址"
|
||||
msgid "Filtering expression"
|
||||
msgstr "过滤表达式"
|
||||
|
||||
#: src/components/header/ProfileMenu.tsx
|
||||
msgid "Force fetching feeds is not yet available."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/LoginPage.tsx
|
||||
msgid "Forgot password?"
|
||||
msgstr "忘记密码?"
|
||||
|
||||
@@ -6,11 +6,13 @@ import "react-contexify/ReactContexify.css"
|
||||
import { App } from "App"
|
||||
import { store } from "app/store"
|
||||
import dayjs from "dayjs"
|
||||
import duration from "dayjs/plugin/duration"
|
||||
import relativeTime from "dayjs/plugin/relativeTime"
|
||||
import ReactDOM from "react-dom/client"
|
||||
import { Provider } from "react-redux"
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
dayjs.extend(duration)
|
||||
|
||||
const root = document.getElementById("root")
|
||||
root &&
|
||||
|
||||
@@ -8,7 +8,7 @@ h|[.header-title]##Configuration property##
|
||||
h|Type
|
||||
h|Default
|
||||
|
||||
a| [[commafeed-server_commafeed-hide-from-web-crawlers]] [.property-path]##`commafeed.hide-from-web-crawlers`##
|
||||
a| [[commafeed-server_commafeed-hide-from-web-crawlers]] [.property-path]##link:#commafeed-server_commafeed-hide-from-web-crawlers[`commafeed.hide-from-web-crawlers`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -25,7 +25,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|boolean
|
||||
|`true`
|
||||
|
||||
a| [[commafeed-server_commafeed-image-proxy-enabled]] [.property-path]##`commafeed.image-proxy-enabled`##
|
||||
a| [[commafeed-server_commafeed-image-proxy-enabled]] [.property-path]##link:#commafeed-server_commafeed-image-proxy-enabled[`commafeed.image-proxy-enabled`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -42,7 +42,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|boolean
|
||||
|`false`
|
||||
|
||||
a| [[commafeed-server_commafeed-password-recovery-enabled]] [.property-path]##`commafeed.password-recovery-enabled`##
|
||||
a| [[commafeed-server_commafeed-password-recovery-enabled]] [.property-path]##link:#commafeed-server_commafeed-password-recovery-enabled[`commafeed.password-recovery-enabled`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -59,7 +59,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|boolean
|
||||
|`false`
|
||||
|
||||
a| [[commafeed-server_commafeed-announcement]] [.property-path]##`commafeed.announcement`##
|
||||
a| [[commafeed-server_commafeed-announcement]] [.property-path]##link:#commafeed-server_commafeed-announcement[`commafeed.announcement`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -76,7 +76,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|string
|
||||
|
|
||||
|
||||
a| [[commafeed-server_commafeed-google-analytics-tracking-code]] [.property-path]##`commafeed.google-analytics-tracking-code`##
|
||||
a| [[commafeed-server_commafeed-google-analytics-tracking-code]] [.property-path]##link:#commafeed-server_commafeed-google-analytics-tracking-code[`commafeed.google-analytics-tracking-code`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -93,7 +93,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|string
|
||||
|
|
||||
|
||||
a| [[commafeed-server_commafeed-google-auth-key]] [.property-path]##`commafeed.google-auth-key`##
|
||||
a| [[commafeed-server_commafeed-google-auth-key]] [.property-path]##link:#commafeed-server_commafeed-google-auth-key[`commafeed.google-auth-key`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -110,11 +110,11 @@ endif::add-copy-button-to-env-var[]
|
||||
|string
|
||||
|
|
||||
|
||||
h|[[commafeed-server_section_commafeed-http-client]] [.section-name.section-level0]##HTTP client configuration##
|
||||
h|[[commafeed-server_section_commafeed-http-client]] [.section-name.section-level0]##link:#commafeed-server_section_commafeed-http-client[HTTP client configuration]##
|
||||
h|Type
|
||||
h|Default
|
||||
|
||||
a| [[commafeed-server_commafeed-http-client-user-agent]] [.property-path]##`commafeed.http-client.user-agent`##
|
||||
a| [[commafeed-server_commafeed-http-client-user-agent]] [.property-path]##link:#commafeed-server_commafeed-http-client-user-agent[`commafeed.http-client.user-agent`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -131,7 +131,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|string
|
||||
|
|
||||
|
||||
a| [[commafeed-server_commafeed-http-client-connect-timeout]] [.property-path]##`commafeed.http-client.connect-timeout`##
|
||||
a| [[commafeed-server_commafeed-http-client-connect-timeout]] [.property-path]##link:#commafeed-server_commafeed-http-client-connect-timeout[`commafeed.http-client.connect-timeout`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -148,7 +148,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`5S`
|
||||
|
||||
a| [[commafeed-server_commafeed-http-client-ssl-handshake-timeout]] [.property-path]##`commafeed.http-client.ssl-handshake-timeout`##
|
||||
a| [[commafeed-server_commafeed-http-client-ssl-handshake-timeout]] [.property-path]##link:#commafeed-server_commafeed-http-client-ssl-handshake-timeout[`commafeed.http-client.ssl-handshake-timeout`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -165,7 +165,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`5S`
|
||||
|
||||
a| [[commafeed-server_commafeed-http-client-socket-timeout]] [.property-path]##`commafeed.http-client.socket-timeout`##
|
||||
a| [[commafeed-server_commafeed-http-client-socket-timeout]] [.property-path]##link:#commafeed-server_commafeed-http-client-socket-timeout[`commafeed.http-client.socket-timeout`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -182,7 +182,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`10S`
|
||||
|
||||
a| [[commafeed-server_commafeed-http-client-response-timeout]] [.property-path]##`commafeed.http-client.response-timeout`##
|
||||
a| [[commafeed-server_commafeed-http-client-response-timeout]] [.property-path]##link:#commafeed-server_commafeed-http-client-response-timeout[`commafeed.http-client.response-timeout`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -199,7 +199,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`10S`
|
||||
|
||||
a| [[commafeed-server_commafeed-http-client-connection-time-to-live]] [.property-path]##`commafeed.http-client.connection-time-to-live`##
|
||||
a| [[commafeed-server_commafeed-http-client-connection-time-to-live]] [.property-path]##link:#commafeed-server_commafeed-http-client-connection-time-to-live[`commafeed.http-client.connection-time-to-live`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -216,7 +216,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`30S`
|
||||
|
||||
a| [[commafeed-server_commafeed-http-client-idle-connections-eviction-interval]] [.property-path]##`commafeed.http-client.idle-connections-eviction-interval`##
|
||||
a| [[commafeed-server_commafeed-http-client-idle-connections-eviction-interval]] [.property-path]##link:#commafeed-server_commafeed-http-client-idle-connections-eviction-interval[`commafeed.http-client.idle-connections-eviction-interval`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -233,7 +233,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`1M`
|
||||
|
||||
a| [[commafeed-server_commafeed-http-client-max-response-size]] [.property-path]##`commafeed.http-client.max-response-size`##
|
||||
a| [[commafeed-server_commafeed-http-client-max-response-size]] [.property-path]##link:#commafeed-server_commafeed-http-client-max-response-size[`commafeed.http-client.max-response-size`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -250,11 +250,11 @@ endif::add-copy-button-to-env-var[]
|
||||
|MemorySize link:#memory-size-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the MemorySize format]]
|
||||
|`5M`
|
||||
|
||||
h|[[commafeed-server_section_commafeed-http-client-cache]] [.section-name.section-level1]##HTTP client cache configuration##
|
||||
h|[[commafeed-server_section_commafeed-http-client-cache]] [.section-name.section-level1]##link:#commafeed-server_section_commafeed-http-client-cache[HTTP client cache configuration]##
|
||||
h|Type
|
||||
h|Default
|
||||
|
||||
a| [[commafeed-server_commafeed-http-client-cache-enabled]] [.property-path]##`commafeed.http-client.cache.enabled`##
|
||||
a| [[commafeed-server_commafeed-http-client-cache-enabled]] [.property-path]##link:#commafeed-server_commafeed-http-client-cache-enabled[`commafeed.http-client.cache.enabled`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -271,7 +271,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|boolean
|
||||
|`true`
|
||||
|
||||
a| [[commafeed-server_commafeed-http-client-cache-maximum-memory-size]] [.property-path]##`commafeed.http-client.cache.maximum-memory-size`##
|
||||
a| [[commafeed-server_commafeed-http-client-cache-maximum-memory-size]] [.property-path]##link:#commafeed-server_commafeed-http-client-cache-maximum-memory-size[`commafeed.http-client.cache.maximum-memory-size`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -288,7 +288,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|MemorySize link:#memory-size-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the MemorySize format]]
|
||||
|`10M`
|
||||
|
||||
a| [[commafeed-server_commafeed-http-client-cache-expiration]] [.property-path]##`commafeed.http-client.cache.expiration`##
|
||||
a| [[commafeed-server_commafeed-http-client-cache-expiration]] [.property-path]##link:#commafeed-server_commafeed-http-client-cache-expiration[`commafeed.http-client.cache.expiration`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -307,11 +307,11 @@ endif::add-copy-button-to-env-var[]
|
||||
|
||||
|
||||
|
||||
h|[[commafeed-server_section_commafeed-feed-refresh]] [.section-name.section-level0]##Feed refresh engine settings##
|
||||
h|[[commafeed-server_section_commafeed-feed-refresh]] [.section-name.section-level0]##link:#commafeed-server_section_commafeed-feed-refresh[Feed refresh engine settings]##
|
||||
h|Type
|
||||
h|Default
|
||||
|
||||
a| [[commafeed-server_commafeed-feed-refresh-interval]] [.property-path]##`commafeed.feed-refresh.interval`##
|
||||
a| [[commafeed-server_commafeed-feed-refresh-interval]] [.property-path]##link:#commafeed-server_commafeed-feed-refresh-interval[`commafeed.feed-refresh.interval`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -328,7 +328,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`5M`
|
||||
|
||||
a| [[commafeed-server_commafeed-feed-refresh-interval-empirical]] [.property-path]##`commafeed.feed-refresh.interval-empirical`##
|
||||
a| [[commafeed-server_commafeed-feed-refresh-interval-empirical]] [.property-path]##link:#commafeed-server_commafeed-feed-refresh-interval-empirical[`commafeed.feed-refresh.interval-empirical`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -345,7 +345,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|boolean
|
||||
|`false`
|
||||
|
||||
a| [[commafeed-server_commafeed-feed-refresh-http-threads]] [.property-path]##`commafeed.feed-refresh.http-threads`##
|
||||
a| [[commafeed-server_commafeed-feed-refresh-http-threads]] [.property-path]##link:#commafeed-server_commafeed-feed-refresh-http-threads[`commafeed.feed-refresh.http-threads`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -362,7 +362,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|int
|
||||
|`3`
|
||||
|
||||
a| [[commafeed-server_commafeed-feed-refresh-database-threads]] [.property-path]##`commafeed.feed-refresh.database-threads`##
|
||||
a| [[commafeed-server_commafeed-feed-refresh-database-threads]] [.property-path]##link:#commafeed-server_commafeed-feed-refresh-database-threads[`commafeed.feed-refresh.database-threads`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -379,7 +379,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|int
|
||||
|`1`
|
||||
|
||||
a| [[commafeed-server_commafeed-feed-refresh-user-inactivity-period]] [.property-path]##`commafeed.feed-refresh.user-inactivity-period`##
|
||||
a| [[commafeed-server_commafeed-feed-refresh-user-inactivity-period]] [.property-path]##link:#commafeed-server_commafeed-feed-refresh-user-inactivity-period[`commafeed.feed-refresh.user-inactivity-period`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -396,7 +396,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`0S`
|
||||
|
||||
a| [[commafeed-server_commafeed-feed-refresh-filtering-expression-evaluation-timeout]] [.property-path]##`commafeed.feed-refresh.filtering-expression-evaluation-timeout`##
|
||||
a| [[commafeed-server_commafeed-feed-refresh-filtering-expression-evaluation-timeout]] [.property-path]##link:#commafeed-server_commafeed-feed-refresh-filtering-expression-evaluation-timeout[`commafeed.feed-refresh.filtering-expression-evaluation-timeout`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -413,12 +413,29 @@ endif::add-copy-button-to-env-var[]
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`500MS`
|
||||
|
||||
a| [[commafeed-server_commafeed-feed-refresh-force-refresh-cooldown-duration]] [.property-path]##link:#commafeed-server_commafeed-feed-refresh-force-refresh-cooldown-duration[`commafeed.feed-refresh.force-refresh-cooldown-duration`]##
|
||||
|
||||
h|[[commafeed-server_section_commafeed-database]] [.section-name.section-level0]##Database settings##
|
||||
[.description]
|
||||
--
|
||||
Duration after which the "Fetch all my feeds now" action is available again after use to avoid spamming feeds.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_FEED_REFRESH_FORCE_REFRESH_COOLDOWN_DURATION+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_FEED_REFRESH_FORCE_REFRESH_COOLDOWN_DURATION+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`0S`
|
||||
|
||||
|
||||
h|[[commafeed-server_section_commafeed-database]] [.section-name.section-level0]##link:#commafeed-server_section_commafeed-database[Database settings]##
|
||||
h|Type
|
||||
h|Default
|
||||
|
||||
a| [[commafeed-server_commafeed-database-query-timeout]] [.property-path]##`commafeed.database.query-timeout`##
|
||||
a| [[commafeed-server_commafeed-database-query-timeout]] [.property-path]##link:#commafeed-server_commafeed-database-query-timeout[`commafeed.database.query-timeout`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -435,11 +452,11 @@ endif::add-copy-button-to-env-var[]
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`0S`
|
||||
|
||||
h|[[commafeed-server_section_commafeed-database-cleanup]] [.section-name.section-level1]##Database cleanup settings##
|
||||
h|[[commafeed-server_section_commafeed-database-cleanup]] [.section-name.section-level1]##link:#commafeed-server_section_commafeed-database-cleanup[Database cleanup settings]##
|
||||
h|Type
|
||||
h|Default
|
||||
|
||||
a| [[commafeed-server_commafeed-database-cleanup-entries-max-age]] [.property-path]##`commafeed.database.cleanup.entries-max-age`##
|
||||
a| [[commafeed-server_commafeed-database-cleanup-entries-max-age]] [.property-path]##link:#commafeed-server_commafeed-database-cleanup-entries-max-age[`commafeed.database.cleanup.entries-max-age`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -456,7 +473,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`365D`
|
||||
|
||||
a| [[commafeed-server_commafeed-database-cleanup-statuses-max-age]] [.property-path]##`commafeed.database.cleanup.statuses-max-age`##
|
||||
a| [[commafeed-server_commafeed-database-cleanup-statuses-max-age]] [.property-path]##link:#commafeed-server_commafeed-database-cleanup-statuses-max-age[`commafeed.database.cleanup.statuses-max-age`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -473,7 +490,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`0S`
|
||||
|
||||
a| [[commafeed-server_commafeed-database-cleanup-max-feed-capacity]] [.property-path]##`commafeed.database.cleanup.max-feed-capacity`##
|
||||
a| [[commafeed-server_commafeed-database-cleanup-max-feed-capacity]] [.property-path]##link:#commafeed-server_commafeed-database-cleanup-max-feed-capacity[`commafeed.database.cleanup.max-feed-capacity`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -490,7 +507,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|int
|
||||
|`500`
|
||||
|
||||
a| [[commafeed-server_commafeed-database-cleanup-max-feeds-per-user]] [.property-path]##`commafeed.database.cleanup.max-feeds-per-user`##
|
||||
a| [[commafeed-server_commafeed-database-cleanup-max-feeds-per-user]] [.property-path]##link:#commafeed-server_commafeed-database-cleanup-max-feeds-per-user[`commafeed.database.cleanup.max-feeds-per-user`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -507,7 +524,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|int
|
||||
|`0`
|
||||
|
||||
a| [[commafeed-server_commafeed-database-cleanup-batch-size]] [.property-path]##`commafeed.database.cleanup.batch-size`##
|
||||
a| [[commafeed-server_commafeed-database-cleanup-batch-size]] [.property-path]##link:#commafeed-server_commafeed-database-cleanup-batch-size[`commafeed.database.cleanup.batch-size`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -526,11 +543,11 @@ endif::add-copy-button-to-env-var[]
|
||||
|
||||
|
||||
|
||||
h|[[commafeed-server_section_commafeed-users]] [.section-name.section-level0]##Users settings##
|
||||
h|[[commafeed-server_section_commafeed-users]] [.section-name.section-level0]##link:#commafeed-server_section_commafeed-users[Users settings]##
|
||||
h|Type
|
||||
h|Default
|
||||
|
||||
a| [[commafeed-server_commafeed-users-allow-registrations]] [.property-path]##`commafeed.users.allow-registrations`##
|
||||
a| [[commafeed-server_commafeed-users-allow-registrations]] [.property-path]##link:#commafeed-server_commafeed-users-allow-registrations[`commafeed.users.allow-registrations`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -547,7 +564,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|boolean
|
||||
|`false`
|
||||
|
||||
a| [[commafeed-server_commafeed-users-strict-password-policy]] [.property-path]##`commafeed.users.strict-password-policy`##
|
||||
a| [[commafeed-server_commafeed-users-strict-password-policy]] [.property-path]##link:#commafeed-server_commafeed-users-strict-password-policy[`commafeed.users.strict-password-policy`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -564,7 +581,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|boolean
|
||||
|`true`
|
||||
|
||||
a| [[commafeed-server_commafeed-users-create-demo-account]] [.property-path]##`commafeed.users.create-demo-account`##
|
||||
a| [[commafeed-server_commafeed-users-create-demo-account]] [.property-path]##link:#commafeed-server_commafeed-users-create-demo-account[`commafeed.users.create-demo-account`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -582,11 +599,11 @@ endif::add-copy-button-to-env-var[]
|
||||
|`false`
|
||||
|
||||
|
||||
h|[[commafeed-server_section_commafeed-websocket]] [.section-name.section-level0]##Websocket settings##
|
||||
h|[[commafeed-server_section_commafeed-websocket]] [.section-name.section-level0]##link:#commafeed-server_section_commafeed-websocket[Websocket settings]##
|
||||
h|Type
|
||||
h|Default
|
||||
|
||||
a| [[commafeed-server_commafeed-websocket-enabled]] [.property-path]##`commafeed.websocket.enabled`##
|
||||
a| [[commafeed-server_commafeed-websocket-enabled]] [.property-path]##link:#commafeed-server_commafeed-websocket-enabled[`commafeed.websocket.enabled`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -603,7 +620,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|boolean
|
||||
|`true`
|
||||
|
||||
a| [[commafeed-server_commafeed-websocket-ping-interval]] [.property-path]##`commafeed.websocket.ping-interval`##
|
||||
a| [[commafeed-server_commafeed-websocket-ping-interval]] [.property-path]##link:#commafeed-server_commafeed-websocket-ping-interval[`commafeed.websocket.ping-interval`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
@@ -620,7 +637,7 @@ endif::add-copy-button-to-env-var[]
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`15M`
|
||||
|
||||
a| [[commafeed-server_commafeed-websocket-tree-reload-interval]] [.property-path]##`commafeed.websocket.tree-reload-interval`##
|
||||
a| [[commafeed-server_commafeed-websocket-tree-reload-interval]] [.property-path]##link:#commafeed-server_commafeed-websocket-tree-reload-interval[`commafeed.websocket.tree-reload-interval`]##
|
||||
|
||||
[.description]
|
||||
--
|
||||
|
||||
@@ -6,16 +6,16 @@
|
||||
<parent>
|
||||
<groupId>com.commafeed</groupId>
|
||||
<artifactId>commafeed</artifactId>
|
||||
<version>5.2.0</version>
|
||||
<version>5.3.1</version>
|
||||
</parent>
|
||||
<artifactId>commafeed-server</artifactId>
|
||||
<name>CommaFeed Server</name>
|
||||
|
||||
<properties>
|
||||
<quarkus.version>3.14.4</quarkus.version>
|
||||
<querydsl.version>6.7</querydsl.version>
|
||||
<quarkus.version>3.15.1</quarkus.version>
|
||||
<querydsl.version>6.8</querydsl.version>
|
||||
<rome.version>2.1.0</rome.version>
|
||||
<swagger.version>2.2.23</swagger.version>
|
||||
<swagger.version>2.2.25</swagger.version>
|
||||
|
||||
<build.database>h2</build.database>
|
||||
</properties>
|
||||
@@ -238,7 +238,7 @@
|
||||
<dependency>
|
||||
<groupId>com.puppycrawl.tools</groupId>
|
||||
<artifactId>checkstyle</artifactId>
|
||||
<version>10.18.1</version>
|
||||
<version>10.18.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<executions>
|
||||
@@ -294,7 +294,7 @@
|
||||
<dependency>
|
||||
<groupId>com.commafeed</groupId>
|
||||
<artifactId>commafeed-client</artifactId>
|
||||
<version>5.2.0</version>
|
||||
<version>5.3.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- compile-time processors -->
|
||||
@@ -358,7 +358,7 @@
|
||||
<dependency>
|
||||
<groupId>io.dropwizard.metrics</groupId>
|
||||
<artifactId>metrics-json</artifactId>
|
||||
<version>4.2.27</version>
|
||||
<version>4.2.28</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
@@ -450,7 +450,7 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents.client5</groupId>
|
||||
<artifactId>httpclient5</artifactId>
|
||||
<version>5.3.1</version>
|
||||
<version>5.4</version>
|
||||
</dependency>
|
||||
<!-- add brotli support for httpclient5 -->
|
||||
<dependency>
|
||||
@@ -461,7 +461,7 @@
|
||||
<dependency>
|
||||
<groupId>io.github.hakky54</groupId>
|
||||
<artifactId>sslcontext-kickstart-for-apache5</artifactId>
|
||||
<version>8.3.6</version>
|
||||
<version>8.3.7</version>
|
||||
</dependency>
|
||||
|
||||
<!-- test dependencies -->
|
||||
|
||||
@@ -209,6 +209,12 @@ public interface CommaFeedConfiguration {
|
||||
*/
|
||||
@WithDefault("500ms")
|
||||
Duration filteringExpressionEvaluationTimeout();
|
||||
|
||||
/**
|
||||
* Duration after which the "Fetch all my feeds now" action is available again after use to avoid spamming feeds.
|
||||
*/
|
||||
@WithDefault("0")
|
||||
Duration forceRefreshCooldownDuration();
|
||||
}
|
||||
|
||||
interface Database {
|
||||
|
||||
@@ -18,7 +18,8 @@ public class JacksonCustomizer implements ObjectMapperCustomizer {
|
||||
objectMapper.registerModule(new JavaTimeModule());
|
||||
|
||||
// read and write instants as milliseconds instead of nanoseconds
|
||||
objectMapper.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false)
|
||||
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true)
|
||||
.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false)
|
||||
.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, false);
|
||||
|
||||
// add support for serializing metrics
|
||||
|
||||
@@ -126,7 +126,13 @@ public class HttpGetter {
|
||||
log.debug("fetching {}", request.getUrl());
|
||||
|
||||
HttpClientContext context = HttpClientContext.create();
|
||||
context.setRequestConfig(RequestConfig.custom().setResponseTimeout(Timeout.of(config.httpClient().responseTimeout())).build());
|
||||
context.setRequestConfig(RequestConfig.custom()
|
||||
.setResponseTimeout(Timeout.of(config.httpClient().responseTimeout()))
|
||||
// causes issues with some feeds
|
||||
// see https://github.com/Athou/commafeed/issues/1572
|
||||
// and https://issues.apache.org/jira/browse/HTTPCLIENT-2344
|
||||
.setProtocolUpgradeEnabled(false)
|
||||
.build());
|
||||
|
||||
return client.execute(request.toClassicHttpRequest(), context, resp -> {
|
||||
byte[] content = resp.getEntity() == null ? null
|
||||
@@ -176,7 +182,7 @@ public class HttpGetter {
|
||||
|
||||
int poolSize = config.feedRefresh().httpThreads();
|
||||
return PoolingHttpClientConnectionManagerBuilder.create()
|
||||
.setSSLSocketFactory(Apache5SslUtils.toSocketFactory(sslFactory))
|
||||
.setTlsSocketStrategy(Apache5SslUtils.toTlsSocketStrategy(sslFactory))
|
||||
.setDefaultConnectionConfig(ConnectionConfig.custom()
|
||||
.setConnectTimeout(Timeout.of(config.httpClient().connectTimeout()))
|
||||
.setSocketTimeout(Timeout.of(config.httpClient().socketTimeout()))
|
||||
|
||||
@@ -52,4 +52,7 @@ public class User extends AbstractModel {
|
||||
|
||||
@Column
|
||||
private Instant recoverPasswordTokenDate;
|
||||
|
||||
@Column
|
||||
private Instant lastForceRefresh;
|
||||
}
|
||||
|
||||
@@ -98,12 +98,19 @@ public class FeedSubscriptionService {
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshAll(User user) {
|
||||
public void refreshAll(User user) throws ForceFeedRefreshTooSoonException {
|
||||
Instant lastForceRefresh = user.getLastForceRefresh();
|
||||
if (lastForceRefresh != null && lastForceRefresh.plus(config.feedRefresh().forceRefreshCooldownDuration()).isAfter(Instant.now())) {
|
||||
throw new ForceFeedRefreshTooSoonException();
|
||||
}
|
||||
|
||||
List<FeedSubscription> subs = feedSubscriptionDAO.findAll(user);
|
||||
for (FeedSubscription sub : subs) {
|
||||
Feed feed = sub.getFeed();
|
||||
feedRefreshEngine.refreshImmediately(feed);
|
||||
}
|
||||
|
||||
user.setLastForceRefresh(Instant.now());
|
||||
}
|
||||
|
||||
public void refreshAllUpForRefresh(User user) {
|
||||
@@ -130,4 +137,11 @@ public class FeedSubscriptionService {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static class ForceFeedRefreshTooSoonException extends Exception {
|
||||
private ForceFeedRefreshTooSoonException() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -43,4 +43,7 @@ public class ServerInfo implements Serializable {
|
||||
@Schema(requiredMode = RequiredMode.REQUIRED)
|
||||
private long treeReloadInterval;
|
||||
|
||||
@Schema(requiredMode = RequiredMode.REQUIRED)
|
||||
private long forceRefreshCooldownDuration;
|
||||
|
||||
}
|
||||
|
||||
@@ -41,4 +41,7 @@ public class UserModel implements Serializable {
|
||||
@Schema(description = "user is admin", requiredMode = RequiredMode.REQUIRED)
|
||||
private boolean admin;
|
||||
|
||||
@Schema(description = "user last force refresh", type = "number")
|
||||
private Instant lastForceRefresh;
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import java.util.Objects;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.apache.hc.core5.http.HttpStatus;
|
||||
import org.jboss.resteasy.reactive.Cache;
|
||||
import org.jboss.resteasy.reactive.RestForm;
|
||||
|
||||
@@ -40,6 +41,7 @@ import com.commafeed.backend.service.FeedEntryFilteringService.FeedEntryFilterEx
|
||||
import com.commafeed.backend.service.FeedEntryService;
|
||||
import com.commafeed.backend.service.FeedService;
|
||||
import com.commafeed.backend.service.FeedSubscriptionService;
|
||||
import com.commafeed.backend.service.FeedSubscriptionService.ForceFeedRefreshTooSoonException;
|
||||
import com.commafeed.frontend.model.Entries;
|
||||
import com.commafeed.frontend.model.Entry;
|
||||
import com.commafeed.frontend.model.FeedInfo;
|
||||
@@ -276,8 +278,13 @@ public class FeedREST {
|
||||
@Operation(summary = "Queue all feeds of the user for refresh", description = "Manually add all feeds of the user to the refresh queue")
|
||||
public Response queueAllForRefresh() {
|
||||
User user = authenticationContext.getCurrentUser();
|
||||
feedSubscriptionService.refreshAll(user);
|
||||
return Response.ok().build();
|
||||
try {
|
||||
feedSubscriptionService.refreshAll(user);
|
||||
return Response.ok().build();
|
||||
} catch (ForceFeedRefreshTooSoonException e) {
|
||||
return Response.status(HttpStatus.SC_TOO_MANY_REQUESTS).build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Path("/refresh")
|
||||
|
||||
@@ -61,6 +61,7 @@ public class ServerREST {
|
||||
infos.setWebsocketEnabled(config.websocket().enabled());
|
||||
infos.setWebsocketPingInterval(config.websocket().pingInterval().toMillis());
|
||||
infos.setTreeReloadInterval(config.websocket().treeReloadInterval().toMillis());
|
||||
infos.setForceRefreshCooldownDuration(config.feedRefresh().forceRefreshCooldownDuration().toMillis());
|
||||
return Response.ok(infos).build();
|
||||
}
|
||||
|
||||
|
||||
@@ -212,6 +212,7 @@ public class UserREST {
|
||||
userModel.setEmail(user.getEmail());
|
||||
userModel.setEnabled(!user.isDisabled());
|
||||
userModel.setApiKey(user.getApiKey());
|
||||
userModel.setLastForceRefresh(user.getLastForceRefresh());
|
||||
for (UserRole role : userRoleDAO.findAll(user)) {
|
||||
if (role.getRole() == Role.ADMIN) {
|
||||
userModel.setAdmin(true);
|
||||
|
||||
@@ -49,6 +49,7 @@ quarkus.native.add-all-charsets=true
|
||||
%test.commafeed.users.allow-registrations=true
|
||||
%test.commafeed.password-recovery-enabled=true
|
||||
%test.commafeed.http-client.cache.enabled=false
|
||||
%test.commafeed.feed-refresh.force-refresh-cooldown-duration=1m
|
||||
|
||||
|
||||
# prod profile overrides
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
|
||||
|
||||
<changeSet id="lastForceRefresh" author="athou">
|
||||
<addColumn tableName="USERS">
|
||||
<column name="lastForceRefresh" type="${timestamp_type}" />
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
@@ -33,5 +33,6 @@
|
||||
<include file="changelogs/db.changelog-4.4.xml" />
|
||||
<include file="changelogs/db.changelog-5.1.xml" />
|
||||
<include file="changelogs/db.changelog-5.2.xml" />
|
||||
<include file="changelogs/db.changelog-5.3.xml" />
|
||||
|
||||
</databaseChangeLog>
|
||||
@@ -239,6 +239,24 @@ class HttpGetterTest {
|
||||
Assertions.assertEquals("ok", new String(result.getContent()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void doesNotUseUpgradeProtocolHeader() {
|
||||
AtomicInteger calls = new AtomicInteger();
|
||||
|
||||
this.mockServerClient.when(HttpRequest.request().withMethod("GET")).respond(req -> {
|
||||
calls.incrementAndGet();
|
||||
|
||||
if (req.containsHeader(HttpHeaders.UPGRADE)) {
|
||||
throw new Exception("upgrade header should not be sent by the client");
|
||||
}
|
||||
|
||||
return HttpResponse.response().withBody("ok");
|
||||
});
|
||||
|
||||
Assertions.assertDoesNotThrow(() -> getter.get(this.feedUrl));
|
||||
Assertions.assertEquals(1, calls.get());
|
||||
}
|
||||
|
||||
@Nested
|
||||
class Compression {
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ public abstract class BaseIT {
|
||||
.as(Entries.class);
|
||||
}
|
||||
|
||||
protected void forceRefreshAllFeeds() {
|
||||
RestAssured.given().get("rest/feed/refreshAll").then().statusCode(HttpStatus.SC_OK);
|
||||
protected int forceRefreshAllFeeds() {
|
||||
return RestAssured.given().get("rest/feed/refreshAll").then().extract().statusCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,11 +183,13 @@ class FeedIT extends BaseIT {
|
||||
|
||||
// mariadb/mysql timestamp precision is 1 second
|
||||
Instant threshold = Instant.now().minus(Duration.ofSeconds(1));
|
||||
forceRefreshAllFeeds();
|
||||
Assertions.assertEquals(HttpStatus.SC_OK, forceRefreshAllFeeds());
|
||||
|
||||
Awaitility.await()
|
||||
.atMost(Duration.ofSeconds(15))
|
||||
.until(() -> getSubscription(subscriptionId), f -> f.getLastRefresh().isAfter(threshold));
|
||||
|
||||
Assertions.assertEquals(HttpStatus.SC_TOO_MANY_REQUESTS, forceRefreshAllFeeds());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ class ServerIT extends BaseIT {
|
||||
Assertions.assertTrue(serverInfos.isWebsocketEnabled());
|
||||
Assertions.assertEquals(900000, serverInfos.getWebsocketPingInterval());
|
||||
Assertions.assertEquals(30000, serverInfos.getTreeReloadInterval());
|
||||
Assertions.assertEquals(60000, serverInfos.getForceRefreshCooldownDuration());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user