forked from Archives/Athou_commafeed
Compare commits
62 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
643c969faf | ||
|
|
85f9469d6d | ||
|
|
0df0d50695 | ||
|
|
5e9256c197 | ||
|
|
b67e1a92f5 | ||
|
|
d250e4bc26 | ||
|
|
dcf1f41f2d | ||
|
|
3df6ba1457 | ||
|
|
b89928f6c6 | ||
|
|
2e014484e3 | ||
|
|
3b2b18fd2e | ||
|
|
ebf1e592ff | ||
|
|
88404b91d8 | ||
|
|
9cbb60313c | ||
|
|
b95d417f5e | ||
|
|
994f1fb121 | ||
|
|
e533c1fa4b | ||
|
|
d0d946ffe9 | ||
|
|
e3bcc824c7 | ||
|
|
357e4e207f | ||
|
|
2aee961600 | ||
|
|
3aa1987319 | ||
|
|
ae15f61fc2 | ||
|
|
e58f92a812 | ||
|
|
46383924b1 | ||
|
|
071920e864 | ||
|
|
012238e6a9 | ||
|
|
a565566c50 | ||
|
|
550804c666 | ||
|
|
f7a4a33f5e | ||
|
|
f1b19ebae3 | ||
|
|
4049fa2e17 | ||
|
|
28808cf4f5 | ||
|
|
870b46cf9d | ||
|
|
9c20dea99c | ||
|
|
63c7679067 | ||
|
|
764c1a6430 | ||
|
|
bb6578bdd0 | ||
|
|
748c8531ad | ||
|
|
a734fe68d2 | ||
|
|
cc5ebc55a4 | ||
|
|
aa396c1e1c | ||
|
|
fbf87ff291 | ||
|
|
e9f3ffddf4 | ||
|
|
695518d68b | ||
|
|
5d96c1e12b | ||
|
|
3a72a1cc04 | ||
|
|
54f5714108 | ||
|
|
04811c7eca | ||
|
|
6856736ddb | ||
|
|
db6c43993a | ||
|
|
508a22576a | ||
|
|
8fb012b3a1 | ||
|
|
133781d314 | ||
|
|
50cb12896e | ||
|
|
79a4315941 | ||
|
|
33a2f76521 | ||
|
|
d4041a1d88 | ||
|
|
09f2f56446 | ||
|
|
a0c3eda506 | ||
|
|
84de3199cc | ||
|
|
a7e8309d63 |
@@ -1,5 +1,12 @@
|
||||
# Changelog
|
||||
|
||||
## [5.1.0]
|
||||
|
||||
- Added a setting for showing/hiding unread count in the browser's tab title/favicon (#1518)
|
||||
- Fixed an issue that could prevent the app from starting on some systems (#1532)
|
||||
- Added a cache busting filter for the webapp index.html and openapi documentation to make sure they are always up to date
|
||||
- Reduced database cleanup log verbosity
|
||||
|
||||
## [5.0.2]
|
||||
|
||||
- Fix favicon fetching for Youtube channels in native mode when Google auth key is set
|
||||
|
||||
@@ -140,7 +140,8 @@ slightly slower throughput.
|
||||
|
||||
IBM provides precompiled binaries for OpenJ9
|
||||
named [Semeru](https://developer.ibm.com/languages/java/semeru-runtimes/downloads/).
|
||||
This is the JVM used in the [Docker image](https://github.com/Athou/commafeed/blob/master/Dockerfile).
|
||||
This is the JVM used in
|
||||
the [Docker image](https://github.com/Athou/commafeed/blob/master/commafeed-server/src/main/docker/Dockerfile.jvm).
|
||||
|
||||
## Translation
|
||||
|
||||
|
||||
541
commafeed-client/package-lock.json
generated
541
commafeed-client/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -15,24 +15,24 @@
|
||||
"i18n:extract": "lingui extract --clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.13.0",
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@fontsource/open-sans": "^5.0.29",
|
||||
"@lingui/core": "^4.11.3",
|
||||
"@lingui/macro": "^4.11.3",
|
||||
"@lingui/react": "^4.11.3",
|
||||
"@mantine/core": "^7.12.1",
|
||||
"@mantine/form": "^7.12.1",
|
||||
"@mantine/hooks": "^7.12.1",
|
||||
"@mantine/modals": "^7.12.1",
|
||||
"@mantine/notifications": "^7.12.1",
|
||||
"@mantine/spotlight": "^7.12.1",
|
||||
"@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",
|
||||
"@monaco-editor/react": "^4.6.0",
|
||||
"@reduxjs/toolkit": "^2.2.7",
|
||||
"axios": "^1.7.4",
|
||||
"dayjs": "^1.11.12",
|
||||
"axios": "^1.7.7",
|
||||
"dayjs": "^1.11.13",
|
||||
"escape-string-regexp": "^5.0.0",
|
||||
"interweave": "^13.1.0",
|
||||
"monaco-editor": "^0.50.0",
|
||||
"monaco-editor": "^0.51.0",
|
||||
"mousetrap": "^1.6.5",
|
||||
"react": "^18.3.1",
|
||||
"react-async-hook": "^4.0.0",
|
||||
@@ -57,10 +57,10 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^1.8.3",
|
||||
"@lingui/cli": "^4.11.3",
|
||||
"@lingui/vite-plugin": "^4.11.3",
|
||||
"@lingui/cli": "^4.11.4",
|
||||
"@lingui/vite-plugin": "^4.11.4",
|
||||
"@types/mousetrap": "^1.6.15",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react": "^18.3.5",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/react-helmet": "^6.1.11",
|
||||
"@types/react-infinite-scroller": "^1.2.5",
|
||||
@@ -71,9 +71,9 @@
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"rollup-plugin-visualizer": "^5.12.0",
|
||||
"typescript": "^5.5.4",
|
||||
"vite": "^5.4.1",
|
||||
"vite": "^5.4.2",
|
||||
"vite-tsconfig-paths": "^5.0.1",
|
||||
"vitest": "^2.0.5",
|
||||
"vitest-mock-extended": "^2.0.0"
|
||||
"vitest-mock-extended": "^2.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,16 +6,16 @@
|
||||
<parent>
|
||||
<groupId>com.commafeed</groupId>
|
||||
<artifactId>commafeed</artifactId>
|
||||
<version>5.0.2</version>
|
||||
<version>5.1.0</version>
|
||||
</parent>
|
||||
<artifactId>commafeed-client</artifactId>
|
||||
<name>CommaFeed Client</name>
|
||||
|
||||
<properties>
|
||||
<!-- renovate: datasource=node-version depName=node -->
|
||||
<node.version>v20.16.0</node.version>
|
||||
<node.version>v20.17.0</node.version>
|
||||
<!-- renovate: datasource=npm depName=npm -->
|
||||
<npm.version>10.8.2</npm.version>
|
||||
<npm.version>10.8.3</npm.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -71,7 +71,7 @@ function Providers(props: { children: React.ReactNode }) {
|
||||
)
|
||||
}
|
||||
|
||||
// swagger-ui is very large, load only on-demand
|
||||
// api documentation page is very large, load only on-demand
|
||||
const ApiDocumentationPage = React.lazy(async () => await import("pages/app/ApiDocumentationPage"))
|
||||
|
||||
function AppRoutes() {
|
||||
@@ -142,16 +142,18 @@ function GoogleAnalyticsHandler() {
|
||||
return null
|
||||
}
|
||||
|
||||
function FaviconHandler() {
|
||||
const root = useAppSelector(state => state.tree.rootCategory)
|
||||
function UnreadCountTitleHandler({ unreadCount, enabled }: { unreadCount: number; enabled?: boolean }) {
|
||||
return <Helmet title={enabled && unreadCount > 0 ? `(${unreadCount}) CommaFeed` : "CommaFeed"} />
|
||||
}
|
||||
|
||||
function UnreadCountFaviconHandler({ unreadCount, enabled }: { unreadCount: number; enabled?: boolean }) {
|
||||
useEffect(() => {
|
||||
const unreadCount = categoryUnreadCount(root)
|
||||
if (unreadCount === 0) {
|
||||
Tinycon.reset()
|
||||
} else {
|
||||
if (enabled && unreadCount > 0) {
|
||||
Tinycon.setBubble(unreadCount)
|
||||
} else {
|
||||
Tinycon.reset()
|
||||
}
|
||||
}, [root])
|
||||
}, [unreadCount, enabled])
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -179,8 +181,13 @@ function CustomCode() {
|
||||
|
||||
export function App() {
|
||||
useI18n()
|
||||
const root = useAppSelector(state => state.tree.rootCategory)
|
||||
const unreadCountTitle = useAppSelector(state => state.user.settings?.unreadCountTitle)
|
||||
const unreadCountFavicon = useAppSelector(state => state.user.settings?.unreadCountFavicon)
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const unreadCount = categoryUnreadCount(root)
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(reloadServerInfos())
|
||||
}, [dispatch])
|
||||
@@ -188,7 +195,8 @@ export function App() {
|
||||
return (
|
||||
<Providers>
|
||||
<>
|
||||
<FaviconHandler />
|
||||
<UnreadCountTitleHandler unreadCount={unreadCount} enabled={unreadCountTitle} />
|
||||
<UnreadCountFaviconHandler unreadCount={unreadCount} enabled={unreadCountFavicon} />
|
||||
<BrowserExtensionBadgeUnreadCountHandler />
|
||||
<HashRouter>
|
||||
<GoogleAnalyticsHandler />
|
||||
|
||||
@@ -248,6 +248,8 @@ export interface Settings {
|
||||
markAllAsReadConfirmation: boolean
|
||||
customContextMenu: boolean
|
||||
mobileFooter: boolean
|
||||
unreadCountTitle: boolean
|
||||
unreadCountFavicon: boolean
|
||||
sharingSettings: SharingSettings
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ import {
|
||||
changeSharingSetting,
|
||||
changeShowRead,
|
||||
changeStarIconDisplayMode,
|
||||
changeUnreadCountFavicon,
|
||||
changeUnreadCountTitle,
|
||||
reloadProfile,
|
||||
reloadSettings,
|
||||
reloadTags,
|
||||
@@ -91,6 +93,14 @@ export const userSlice = createSlice({
|
||||
if (!state.settings) return
|
||||
state.settings.mobileFooter = action.meta.arg
|
||||
})
|
||||
builder.addCase(changeUnreadCountTitle.pending, (state, action) => {
|
||||
if (!state.settings) return
|
||||
state.settings.unreadCountTitle = action.meta.arg
|
||||
})
|
||||
builder.addCase(changeUnreadCountFavicon.pending, (state, action) => {
|
||||
if (!state.settings) return
|
||||
state.settings.unreadCountFavicon = action.meta.arg
|
||||
})
|
||||
builder.addCase(changeSharingSetting.pending, (state, action) => {
|
||||
if (!state.settings) return
|
||||
state.settings.sharingSettings[action.meta.arg.site] = action.meta.arg.value
|
||||
@@ -107,6 +117,8 @@ export const userSlice = createSlice({
|
||||
changeMarkAllAsReadConfirmation.fulfilled,
|
||||
changeCustomContextMenu.fulfilled,
|
||||
changeMobileFooter.fulfilled,
|
||||
changeUnreadCountTitle.fulfilled,
|
||||
changeUnreadCountFavicon.fulfilled,
|
||||
changeSharingSetting.fulfilled
|
||||
),
|
||||
() => {
|
||||
|
||||
@@ -77,6 +77,16 @@ export const changeMobileFooter = createAppAsyncThunk("settings/mobileFooter", (
|
||||
if (!settings) return
|
||||
client.user.saveSettings({ ...settings, mobileFooter })
|
||||
})
|
||||
export const changeUnreadCountTitle = createAppAsyncThunk("settings/unreadCountTitle", (unreadCountTitle: boolean, thunkApi) => {
|
||||
const { settings } = thunkApi.getState().user
|
||||
if (!settings) return
|
||||
client.user.saveSettings({ ...settings, unreadCountTitle })
|
||||
})
|
||||
export const changeUnreadCountFavicon = createAppAsyncThunk("settings/unreadCountFavicon", (unreadCountFavicon: boolean, thunkApi) => {
|
||||
const { settings } = thunkApi.getState().user
|
||||
if (!settings) return
|
||||
client.user.saveSettings({ ...settings, unreadCountFavicon })
|
||||
})
|
||||
export const changeSharingSetting = createAppAsyncThunk(
|
||||
"settings/sharingSetting",
|
||||
(
|
||||
|
||||
@@ -17,6 +17,8 @@ import {
|
||||
changeSharingSetting,
|
||||
changeShowRead,
|
||||
changeStarIconDisplayMode,
|
||||
changeUnreadCountFavicon,
|
||||
changeUnreadCountTitle,
|
||||
} from "app/user/thunks"
|
||||
import { locales } from "i18n"
|
||||
import type { ReactNode } from "react"
|
||||
@@ -32,6 +34,8 @@ export function DisplaySettings() {
|
||||
const markAllAsReadConfirmation = useAppSelector(state => state.user.settings?.markAllAsReadConfirmation)
|
||||
const customContextMenu = useAppSelector(state => state.user.settings?.customContextMenu)
|
||||
const mobileFooter = useAppSelector(state => state.user.settings?.mobileFooter)
|
||||
const unreadCountTitle = useAppSelector(state => state.user.settings?.unreadCountTitle)
|
||||
const unreadCountFavicon = useAppSelector(state => state.user.settings?.unreadCountFavicon)
|
||||
const sharingSettings = useAppSelector(state => state.user.settings?.sharingSettings)
|
||||
const dispatch = useAppDispatch()
|
||||
const { _ } = useLingui()
|
||||
@@ -91,6 +95,20 @@ export function DisplaySettings() {
|
||||
onChange={async e => await dispatch(changeMobileFooter(e.currentTarget.checked))}
|
||||
/>
|
||||
|
||||
<Divider label={<Trans>Browser tab</Trans>} labelPosition="center" />
|
||||
|
||||
<Switch
|
||||
label={<Trans>Show unread count in tab title</Trans>}
|
||||
checked={unreadCountTitle}
|
||||
onChange={async e => await dispatch(changeUnreadCountTitle(e.currentTarget.checked))}
|
||||
/>
|
||||
|
||||
<Switch
|
||||
label={<Trans>Show unread count in tab favicon</Trans>}
|
||||
checked={unreadCountFavicon}
|
||||
onChange={async e => await dispatch(changeUnreadCountFavicon(e.currentTarget.checked))}
|
||||
/>
|
||||
|
||||
<Divider label={<Trans>Entry headers</Trans>} labelPosition="center" />
|
||||
|
||||
<Select
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr ""
|
||||
msgid "Browser extention"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr ""
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr "Extensió del navegador necessària per a Chrome"
|
||||
msgid "Browser extention"
|
||||
msgstr "Extensió del navegador"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr "Mostra el menú natiu (escriptori)"
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr ""
|
||||
msgid "Browser extention"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr ""
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr ""
|
||||
msgid "Browser extention"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr ""
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr ""
|
||||
msgid "Browser extention"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr ""
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr "Browser-Erweiterung für Chrome benötigt"
|
||||
msgid "Browser extention"
|
||||
msgstr "Browser-Erweiterung"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr "Natives Menü anzeigen (Desktop)"
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr "Browser extension required for Chrome"
|
||||
msgid "Browser extention"
|
||||
msgstr "Browser extention"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr "Browser tab"
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr "Show native menu (desktop)"
|
||||
msgid "Show star icon"
|
||||
msgstr "Show star icon"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr "Show unread count in tab favicon"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr "Show unread count in tab title"
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr ""
|
||||
msgid "Browser extention"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr ""
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr ""
|
||||
msgid "Browser extention"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr ""
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr ""
|
||||
msgid "Browser extention"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr ""
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr "L'extension navigateur est nécessaire sur Chrome"
|
||||
msgid "Browser extention"
|
||||
msgstr "Extension navigateur"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr "Onglet navigateur"
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -178,7 +182,7 @@ msgstr "Fermer le menu"
|
||||
|
||||
#: src/components/KeyboardShortcutsHelp.tsx
|
||||
msgid "Cmd"
|
||||
msgstr ""
|
||||
msgstr "Cmd"
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "CommaFeed browser extension version {browserExtensionVersion}."
|
||||
@@ -319,7 +323,7 @@ msgstr "Entrez votre mot de passe actuel pour changer les paramètres du profil"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Entry headers"
|
||||
msgstr ""
|
||||
msgstr "En-têtes de l'entrée"
|
||||
|
||||
#: src/components/Alert.tsx
|
||||
msgid "Error"
|
||||
@@ -582,7 +586,7 @@ msgstr "Fin de la liste"
|
||||
|
||||
#: src/components/content/ShareButtons.tsx
|
||||
msgid "No sharing options available."
|
||||
msgstr ""
|
||||
msgstr "Aucune option de partage disponible"
|
||||
|
||||
#: src/components/sidebar/TreeSearch.tsx
|
||||
msgid "Nothing found"
|
||||
@@ -594,11 +598,11 @@ msgstr "Du plus ancien au plus récent"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "On desktop"
|
||||
msgstr ""
|
||||
msgstr "Version PC"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "On mobile"
|
||||
msgstr ""
|
||||
msgstr "Version mobile"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "On mobile, show action buttons at the bottom of the screen"
|
||||
@@ -664,7 +668,7 @@ msgstr "Fichier OPML"
|
||||
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
msgid "OPML file is required"
|
||||
msgstr ""
|
||||
msgstr "Vous devez fournir un fichier OPML"
|
||||
|
||||
#: src/pages/app/AboutPage.tsx
|
||||
msgid "Order"
|
||||
@@ -808,7 +812,7 @@ msgstr "Afficher les options de l'entrée (mobile)"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show external link icon"
|
||||
msgstr ""
|
||||
msgstr "Afficher l'icône du lien distant"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show feeds and categories with no unread entries"
|
||||
@@ -824,7 +828,15 @@ msgstr "Afficher les options du navigateur (ordinateur)"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
msgstr "Afficher l'icône Favori"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr "Afficher le nombre d'entrées non lues dans la favicône de l'onglet"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr "Afficher le nombre d'entrées non lues dans le titre de l'onglet"
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr ""
|
||||
msgid "Browser extention"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr ""
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr ""
|
||||
msgid "Browser extention"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr ""
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr ""
|
||||
msgid "Browser extention"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr ""
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr ""
|
||||
msgid "Browser extention"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr ""
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr ""
|
||||
msgid "Browser extention"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr ""
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr ""
|
||||
msgid "Browser extention"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr ""
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr ""
|
||||
msgid "Browser extention"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr ""
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr ""
|
||||
msgid "Browser extention"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr ""
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr ""
|
||||
msgid "Browser extention"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr ""
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr ""
|
||||
msgid "Browser extention"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr ""
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr ""
|
||||
msgid "Browser extention"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr ""
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr ""
|
||||
msgid "Browser extention"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr ""
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr "Для браузера Chrome требуется расширение"
|
||||
msgid "Browser extention"
|
||||
msgstr "Расширение для браузера"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr "Показать родное меню (ПК)"
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr ""
|
||||
msgid "Browser extention"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr ""
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr ""
|
||||
msgid "Browser extention"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr ""
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr ""
|
||||
msgid "Browser extention"
|
||||
msgstr "Tarayıcı eklentisi"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr "Orijinal tarayıcı menüsünü göster (masaüstü)"
|
||||
msgid "Show star icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -140,6 +140,10 @@ msgstr "浏览器扩展"
|
||||
msgid "Browser extention"
|
||||
msgstr "浏览器扩展"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Browser tab"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/admin/UserEdit.tsx
|
||||
#: src/components/content/add/AddCategory.tsx
|
||||
#: src/components/content/add/ImportOpml.tsx
|
||||
@@ -826,6 +830,14 @@ msgstr "显示原生菜单(桌面端)"
|
||||
msgid "Show star icon"
|
||||
msgstr "显示星标图标"
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab favicon"
|
||||
msgstr ""
|
||||
|
||||
#: src/components/settings/DisplaySettings.tsx
|
||||
msgid "Show unread count in tab title"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/auth/RegistrationPage.tsx
|
||||
#: src/pages/WelcomePage.tsx
|
||||
|
||||
@@ -1,370 +1,373 @@
|
||||
|
||||
:summaryTableId: commafeed
|
||||
[.configuration-legend]
|
||||
:summaryTableId: commafeed-server_commafeed
|
||||
[.configuration-legend]
|
||||
icon:lock[title=Fixed at build time] Configuration property fixed at build time - All other configuration properties are overridable at runtime
|
||||
[.configuration-reference.searchable, cols="80,.^10,.^10"]
|
||||
|===
|
||||
|
||||
h|[[commafeed_configuration]]link:#commafeed_configuration[Configuration property]
|
||||
|
||||
h|[.header-title]##Configuration property##
|
||||
h|Type
|
||||
h|Default
|
||||
|
||||
a| [[commafeed_commafeed-hide-from-web-crawlers]]`link:#commafeed_commafeed-hide-from-web-crawlers[commafeed.hide-from-web-crawlers]`
|
||||
|
||||
a| [[commafeed-server_commafeed-hide-from-web-crawlers]] [.property-path]##`commafeed.hide-from-web-crawlers`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Whether to expose a robots.txt file that disallows web crawlers and search engine indexers.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_HIDE_FROM_WEB_CRAWLERS+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_HIDE_FROM_WEB_CRAWLERS+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|boolean
|
||||
--
|
||||
|boolean
|
||||
|`true`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-image-proxy-enabled]]`link:#commafeed_commafeed-image-proxy-enabled[commafeed.image-proxy-enabled]`
|
||||
|
||||
a| [[commafeed-server_commafeed-image-proxy-enabled]] [.property-path]##`commafeed.image-proxy-enabled`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
If enabled, images in feed entries will be proxied through the server instead of accessed directly by the browser. This is useful if commafeed is accessed through a restricting proxy that blocks some feeds that are followed.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_IMAGE_PROXY_ENABLED+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_IMAGE_PROXY_ENABLED+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|boolean
|
||||
--
|
||||
|boolean
|
||||
|`false`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-password-recovery-enabled]]`link:#commafeed_commafeed-password-recovery-enabled[commafeed.password-recovery-enabled]`
|
||||
|
||||
a| [[commafeed-server_commafeed-password-recovery-enabled]] [.property-path]##`commafeed.password-recovery-enabled`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Enable password recovery via email. Quarkus mailer will need to be configured.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_PASSWORD_RECOVERY_ENABLED+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_PASSWORD_RECOVERY_ENABLED+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|boolean
|
||||
--
|
||||
|boolean
|
||||
|`false`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-announcement]]`link:#commafeed_commafeed-announcement[commafeed.announcement]`
|
||||
|
||||
a| [[commafeed-server_commafeed-announcement]] [.property-path]##`commafeed.announcement`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Message displayed in a notification at the bottom of the page.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_ANNOUNCEMENT+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_ANNOUNCEMENT+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|string
|
||||
--
|
||||
|string
|
||||
|
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-google-analytics-tracking-code]]`link:#commafeed_commafeed-google-analytics-tracking-code[commafeed.google-analytics-tracking-code]`
|
||||
|
||||
a| [[commafeed-server_commafeed-google-analytics-tracking-code]] [.property-path]##`commafeed.google-analytics-tracking-code`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Google Analytics tracking code.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_GOOGLE_ANALYTICS_TRACKING_CODE+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_GOOGLE_ANALYTICS_TRACKING_CODE+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|string
|
||||
--
|
||||
|string
|
||||
|
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-google-auth-key]]`link:#commafeed_commafeed-google-auth-key[commafeed.google-auth-key]`
|
||||
|
||||
a| [[commafeed-server_commafeed-google-auth-key]] [.property-path]##`commafeed.google-auth-key`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Google Auth key for fetching Youtube channel favicons.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_GOOGLE_AUTH_KEY+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_GOOGLE_AUTH_KEY+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|string
|
||||
--
|
||||
|string
|
||||
|
|
||||
|
||||
h|[[commafeed-server_section_commafeed-http-client]] [.section-name.section-level0]##HTTP client configuration##
|
||||
h|Type
|
||||
h|Default
|
||||
|
||||
a| [[commafeed_commafeed-http-client-user-agent]]`link:#commafeed_commafeed-http-client-user-agent[commafeed.http-client.user-agent]`
|
||||
|
||||
a| [[commafeed-server_commafeed-http-client-user-agent]] [.property-path]##`commafeed.http-client.user-agent`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
User-Agent string that will be used by the http client, leave empty for the default one.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_HTTP_CLIENT_USER_AGENT+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_HTTP_CLIENT_USER_AGENT+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|string
|
||||
--
|
||||
|string
|
||||
|
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-http-client-connect-timeout]]`link:#commafeed_commafeed-http-client-connect-timeout[commafeed.http-client.connect-timeout]`
|
||||
|
||||
a| [[commafeed-server_commafeed-http-client-connect-timeout]] [.property-path]##`commafeed.http-client.connect-timeout`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Time to wait for a connection to be established.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_HTTP_CLIENT_CONNECT_TIMEOUT+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_HTTP_CLIENT_CONNECT_TIMEOUT+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|link:https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html[Duration]
|
||||
link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
--
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`5S`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-http-client-ssl-handshake-timeout]]`link:#commafeed_commafeed-http-client-ssl-handshake-timeout[commafeed.http-client.ssl-handshake-timeout]`
|
||||
|
||||
a| [[commafeed-server_commafeed-http-client-ssl-handshake-timeout]] [.property-path]##`commafeed.http-client.ssl-handshake-timeout`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Time to wait for SSL handshake to complete.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_HTTP_CLIENT_SSL_HANDSHAKE_TIMEOUT+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_HTTP_CLIENT_SSL_HANDSHAKE_TIMEOUT+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|link:https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html[Duration]
|
||||
link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
--
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`5S`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-http-client-socket-timeout]]`link:#commafeed_commafeed-http-client-socket-timeout[commafeed.http-client.socket-timeout]`
|
||||
|
||||
a| [[commafeed-server_commafeed-http-client-socket-timeout]] [.property-path]##`commafeed.http-client.socket-timeout`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Time to wait between two packets before timeout.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_HTTP_CLIENT_SOCKET_TIMEOUT+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_HTTP_CLIENT_SOCKET_TIMEOUT+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|link:https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html[Duration]
|
||||
link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
--
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`10S`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-http-client-response-timeout]]`link:#commafeed_commafeed-http-client-response-timeout[commafeed.http-client.response-timeout]`
|
||||
|
||||
a| [[commafeed-server_commafeed-http-client-response-timeout]] [.property-path]##`commafeed.http-client.response-timeout`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Time to wait for the full response to be received.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_HTTP_CLIENT_RESPONSE_TIMEOUT+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_HTTP_CLIENT_RESPONSE_TIMEOUT+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|link:https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html[Duration]
|
||||
link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
--
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`10S`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-http-client-connection-time-to-live]]`link:#commafeed_commafeed-http-client-connection-time-to-live[commafeed.http-client.connection-time-to-live]`
|
||||
|
||||
a| [[commafeed-server_commafeed-http-client-connection-time-to-live]] [.property-path]##`commafeed.http-client.connection-time-to-live`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Time to live for a connection in the pool.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_HTTP_CLIENT_CONNECTION_TIME_TO_LIVE+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_HTTP_CLIENT_CONNECTION_TIME_TO_LIVE+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|link:https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html[Duration]
|
||||
link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
--
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`30S`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-http-client-idle-connections-eviction-interval]]`link:#commafeed_commafeed-http-client-idle-connections-eviction-interval[commafeed.http-client.idle-connections-eviction-interval]`
|
||||
|
||||
a| [[commafeed-server_commafeed-http-client-idle-connections-eviction-interval]] [.property-path]##`commafeed.http-client.idle-connections-eviction-interval`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Time between eviction runs for idle connections.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_HTTP_CLIENT_IDLE_CONNECTIONS_EVICTION_INTERVAL+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_HTTP_CLIENT_IDLE_CONNECTIONS_EVICTION_INTERVAL+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|link:https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html[Duration]
|
||||
link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
--
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`1M`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-http-client-max-response-size]]`link:#commafeed_commafeed-http-client-max-response-size[commafeed.http-client.max-response-size]`
|
||||
|
||||
a| [[commafeed-server_commafeed-http-client-max-response-size]] [.property-path]##`commafeed.http-client.max-response-size`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
If a feed is larger than this, it will be discarded to prevent memory issues while parsing the feed.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_HTTP_CLIENT_MAX_RESPONSE_SIZE+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_HTTP_CLIENT_MAX_RESPONSE_SIZE+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|MemorySize link:#memory-size-note-anchor[icon:question-circle[title=More information about the MemorySize format]]
|
||||
--
|
||||
|MemorySize link:#memory-size-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the MemorySize format]]
|
||||
|`5M`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-feed-refresh-interval]]`link:#commafeed_commafeed-feed-refresh-interval[commafeed.feed-refresh.interval]`
|
||||
h|[[commafeed-server_section_commafeed-feed-refresh]] [.section-name.section-level0]##Feed refresh engine settings##
|
||||
h|Type
|
||||
h|Default
|
||||
|
||||
a| [[commafeed-server_commafeed-feed-refresh-interval]] [.property-path]##`commafeed.feed-refresh.interval`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Amount of time CommaFeed will wait before refreshing the same feed.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_FEED_REFRESH_INTERVAL+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_FEED_REFRESH_INTERVAL+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|link:https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html[Duration]
|
||||
link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
--
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`5M`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-feed-refresh-interval-empirical]]`link:#commafeed_commafeed-feed-refresh-interval-empirical[commafeed.feed-refresh.interval-empirical]`
|
||||
|
||||
a| [[commafeed-server_commafeed-feed-refresh-interval-empirical]] [.property-path]##`commafeed.feed-refresh.interval-empirical`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
If true, CommaFeed will calculate the next refresh time based on the feed's average time between entries and the time since the last entry was published. The interval will be somewhere between the default refresh interval and 24h. See `FeedRefreshIntervalCalculator` for details.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_FEED_REFRESH_INTERVAL_EMPIRICAL+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_FEED_REFRESH_INTERVAL_EMPIRICAL+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|boolean
|
||||
--
|
||||
|boolean
|
||||
|`false`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-feed-refresh-http-threads]]`link:#commafeed_commafeed-feed-refresh-http-threads[commafeed.feed-refresh.http-threads]`
|
||||
|
||||
a| [[commafeed-server_commafeed-feed-refresh-http-threads]] [.property-path]##`commafeed.feed-refresh.http-threads`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Amount of http threads used to fetch feeds.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_FEED_REFRESH_HTTP_THREADS+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_FEED_REFRESH_HTTP_THREADS+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|@jakarta.validation.constraints.Min(1L) int
|
||||
--
|
||||
|@jakarta.validation.constraints.Min(1L) int
|
||||
|`3`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-feed-refresh-database-threads]]`link:#commafeed_commafeed-feed-refresh-database-threads[commafeed.feed-refresh.database-threads]`
|
||||
|
||||
a| [[commafeed-server_commafeed-feed-refresh-database-threads]] [.property-path]##`commafeed.feed-refresh.database-threads`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Amount of threads used to insert new entries in the database.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_FEED_REFRESH_DATABASE_THREADS+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_FEED_REFRESH_DATABASE_THREADS+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|@jakarta.validation.constraints.Min(1L) int
|
||||
--
|
||||
|@jakarta.validation.constraints.Min(1L) int
|
||||
|`1`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-feed-refresh-user-inactivity-period]]`link:#commafeed_commafeed-feed-refresh-user-inactivity-period[commafeed.feed-refresh.user-inactivity-period]`
|
||||
|
||||
a| [[commafeed-server_commafeed-feed-refresh-user-inactivity-period]] [.property-path]##`commafeed.feed-refresh.user-inactivity-period`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Duration after which a user is considered inactive. Feeds for inactive users are not refreshed until they log in again. 0 to disable.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_FEED_REFRESH_USER_INACTIVITY_PERIOD+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_FEED_REFRESH_USER_INACTIVITY_PERIOD+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|link:https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html[Duration]
|
||||
link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
--
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`0S`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-feed-refresh-filtering-expression-evaluation-timeout]]`link:#commafeed_commafeed-feed-refresh-filtering-expression-evaluation-timeout[commafeed.feed-refresh.filtering-expression-evaluation-timeout]`
|
||||
|
||||
a| [[commafeed-server_commafeed-feed-refresh-filtering-expression-evaluation-timeout]] [.property-path]##`commafeed.feed-refresh.filtering-expression-evaluation-timeout`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Duration after which the evaluation of a filtering expresion to mark an entry as read is considered to have timed out.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_FEED_REFRESH_FILTERING_EXPRESSION_EVALUATION_TIMEOUT+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_FEED_REFRESH_FILTERING_EXPRESSION_EVALUATION_TIMEOUT+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|link:https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html[Duration]
|
||||
link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
--
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`500MS`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-database-query-timeout]]`link:#commafeed_commafeed-database-query-timeout[commafeed.database.query-timeout]`
|
||||
h|[[commafeed-server_section_commafeed-database]] [.section-name.section-level0]##Database settings##
|
||||
h|Type
|
||||
h|Default
|
||||
|
||||
a| [[commafeed-server_commafeed-database-query-timeout]] [.property-path]##`commafeed.database.query-timeout`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Database query timeout. 0 to disable.
|
||||
Timeout applied to all database queries. 0 to disable.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_DATABASE_QUERY_TIMEOUT+++[]
|
||||
@@ -372,205 +375,218 @@ endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_DATABASE_QUERY_TIMEOUT+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|link:https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html[Duration]
|
||||
link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
--
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/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|Type
|
||||
h|Default
|
||||
|
||||
a| [[commafeed_commafeed-database-cleanup-entries-max-age]]`link:#commafeed_commafeed-database-cleanup-entries-max-age[commafeed.database.cleanup.entries-max-age]`
|
||||
|
||||
a| [[commafeed-server_commafeed-database-cleanup-entries-max-age]] [.property-path]##`commafeed.database.cleanup.entries-max-age`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Maximum age of feed entries in the database. Older entries will be deleted. 0 to disable.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_DATABASE_CLEANUP_ENTRIES_MAX_AGE+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_DATABASE_CLEANUP_ENTRIES_MAX_AGE+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|link:https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html[Duration]
|
||||
link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
--
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`365D`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-database-cleanup-statuses-max-age]]`link:#commafeed_commafeed-database-cleanup-statuses-max-age[commafeed.database.cleanup.statuses-max-age]`
|
||||
|
||||
a| [[commafeed-server_commafeed-database-cleanup-statuses-max-age]] [.property-path]##`commafeed.database.cleanup.statuses-max-age`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Maximum age of feed entry statuses (read/unread) in the database. Older statuses will be deleted. 0 to disable.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_DATABASE_CLEANUP_STATUSES_MAX_AGE+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_DATABASE_CLEANUP_STATUSES_MAX_AGE+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|link:https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html[Duration]
|
||||
link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
--
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`0S`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-database-cleanup-max-feed-capacity]]`link:#commafeed_commafeed-database-cleanup-max-feed-capacity[commafeed.database.cleanup.max-feed-capacity]`
|
||||
|
||||
a| [[commafeed-server_commafeed-database-cleanup-max-feed-capacity]] [.property-path]##`commafeed.database.cleanup.max-feed-capacity`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Maximum number of entries per feed to keep in the database. 0 to disable.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_DATABASE_CLEANUP_MAX_FEED_CAPACITY+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_DATABASE_CLEANUP_MAX_FEED_CAPACITY+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|int
|
||||
--
|
||||
|int
|
||||
|`500`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-database-cleanup-max-feeds-per-user]]`link:#commafeed_commafeed-database-cleanup-max-feeds-per-user[commafeed.database.cleanup.max-feeds-per-user]`
|
||||
|
||||
a| [[commafeed-server_commafeed-database-cleanup-max-feeds-per-user]] [.property-path]##`commafeed.database.cleanup.max-feeds-per-user`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Limit the number of feeds a user can subscribe to. 0 to disable.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_DATABASE_CLEANUP_MAX_FEEDS_PER_USER+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_DATABASE_CLEANUP_MAX_FEEDS_PER_USER+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|int
|
||||
--
|
||||
|int
|
||||
|`0`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-database-cleanup-batch-size]]`link:#commafeed_commafeed-database-cleanup-batch-size[commafeed.database.cleanup.batch-size]`
|
||||
|
||||
a| [[commafeed-server_commafeed-database-cleanup-batch-size]] [.property-path]##`commafeed.database.cleanup.batch-size`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Rows to delete per query while cleaning up old entries.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_DATABASE_CLEANUP_BATCH_SIZE+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_DATABASE_CLEANUP_BATCH_SIZE+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|@jakarta.validation.constraints.Positive int
|
||||
--
|
||||
|@jakarta.validation.constraints.Positive int
|
||||
|`100`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-users-allow-registrations]]`link:#commafeed_commafeed-users-allow-registrations[commafeed.users.allow-registrations]`
|
||||
|
||||
h|[[commafeed-server_section_commafeed-users]] [.section-name.section-level0]##Users settings##
|
||||
h|Type
|
||||
h|Default
|
||||
|
||||
a| [[commafeed-server_commafeed-users-allow-registrations]] [.property-path]##`commafeed.users.allow-registrations`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Whether to let users create accounts for themselves.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_USERS_ALLOW_REGISTRATIONS+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_USERS_ALLOW_REGISTRATIONS+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|boolean
|
||||
--
|
||||
|boolean
|
||||
|`false`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-users-strict-password-policy]]`link:#commafeed_commafeed-users-strict-password-policy[commafeed.users.strict-password-policy]`
|
||||
|
||||
a| [[commafeed-server_commafeed-users-strict-password-policy]] [.property-path]##`commafeed.users.strict-password-policy`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Whether to enable strict password validation (1 uppercase char, 1 lowercase char, 1 digit, 1 special char).
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_USERS_STRICT_PASSWORD_POLICY+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_USERS_STRICT_PASSWORD_POLICY+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|boolean
|
||||
--
|
||||
|boolean
|
||||
|`true`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-users-create-demo-account]]`link:#commafeed_commafeed-users-create-demo-account[commafeed.users.create-demo-account]`
|
||||
|
||||
a| [[commafeed-server_commafeed-users-create-demo-account]] [.property-path]##`commafeed.users.create-demo-account`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Whether to create a demo account the first time the app starts.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_USERS_CREATE_DEMO_ACCOUNT+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_USERS_CREATE_DEMO_ACCOUNT+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|boolean
|
||||
--
|
||||
|boolean
|
||||
|`false`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-websocket-enabled]]`link:#commafeed_commafeed-websocket-enabled[commafeed.websocket.enabled]`
|
||||
h|[[commafeed-server_section_commafeed-websocket]] [.section-name.section-level0]##Websocket settings##
|
||||
h|Type
|
||||
h|Default
|
||||
|
||||
a| [[commafeed-server_commafeed-websocket-enabled]] [.property-path]##`commafeed.websocket.enabled`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Enable websocket connection so the server can notify web clients that there are new entries for feeds.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_WEBSOCKET_ENABLED+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_WEBSOCKET_ENABLED+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|boolean
|
||||
--
|
||||
|boolean
|
||||
|`true`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-websocket-ping-interval]]`link:#commafeed_commafeed-websocket-ping-interval[commafeed.websocket.ping-interval]`
|
||||
|
||||
a| [[commafeed-server_commafeed-websocket-ping-interval]] [.property-path]##`commafeed.websocket.ping-interval`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
Interval at which the client will send a ping message on the websocket to keep the connection alive.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_WEBSOCKET_PING_INTERVAL+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_WEBSOCKET_PING_INTERVAL+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|link:https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html[Duration]
|
||||
link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
--
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`15M`
|
||||
|
||||
|
||||
a| [[commafeed_commafeed-websocket-tree-reload-interval]]`link:#commafeed_commafeed-websocket-tree-reload-interval[commafeed.websocket.tree-reload-interval]`
|
||||
|
||||
a| [[commafeed-server_commafeed-websocket-tree-reload-interval]] [.property-path]##`commafeed.websocket.tree-reload-interval`##
|
||||
|
||||
[.description]
|
||||
--
|
||||
If the websocket connection is disabled or the connection is lost, the client will reload the feed tree at this interval.
|
||||
|
||||
|
||||
ifdef::add-copy-button-to-env-var[]
|
||||
Environment variable: env_var_with_copy_button:+++COMMAFEED_WEBSOCKET_TREE_RELOAD_INTERVAL+++[]
|
||||
endif::add-copy-button-to-env-var[]
|
||||
ifndef::add-copy-button-to-env-var[]
|
||||
Environment variable: `+++COMMAFEED_WEBSOCKET_TREE_RELOAD_INTERVAL+++`
|
||||
endif::add-copy-button-to-env-var[]
|
||||
--|link:https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html[Duration]
|
||||
link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
--
|
||||
|link:https://docs.oracle.com/en/java/javase/17/docs/api/java/time/Duration.html[Duration] link:#duration-note-anchor-{summaryTableId}[icon:question-circle[title=More information about the Duration format]]
|
||||
|`30S`
|
||||
|
||||
|
||||
|===
|
||||
|
||||
ifndef::no-duration-note[]
|
||||
[NOTE]
|
||||
[id='duration-note-anchor-{summaryTableId}']
|
||||
[id=duration-note-anchor-commafeed-server_commafeed]
|
||||
.About the Duration format
|
||||
====
|
||||
To write duration values, use the standard `java.time.Duration` format.
|
||||
@@ -587,11 +603,15 @@ In other cases, the simplified format is translated to the `java.time.Duration`
|
||||
* If the value is a number followed by `d`, it is prefixed with `P`.
|
||||
====
|
||||
endif::no-duration-note[]
|
||||
|
||||
ifndef::no-memory-size-note[]
|
||||
[NOTE]
|
||||
[[memory-size-note-anchor]]
|
||||
[id=memory-size-note-anchor-commafeed-server_commafeed]
|
||||
.About the MemorySize format
|
||||
====
|
||||
A size configuration option recognises string in this format (shown as a regular expression): `[0-9]+[KkMmGgTtPpEeZzYy]?`.
|
||||
A size configuration option recognizes strings in this format (shown as a regular expression): `[0-9]+[KkMmGgTtPpEeZzYy]?`.
|
||||
|
||||
If no suffix is given, assume bytes.
|
||||
====
|
||||
ifndef::no-memory-size-note[]
|
||||
|
||||
:!summaryTableId:
|
||||
@@ -6,15 +6,16 @@
|
||||
<parent>
|
||||
<groupId>com.commafeed</groupId>
|
||||
<artifactId>commafeed</artifactId>
|
||||
<version>5.0.2</version>
|
||||
<version>5.1.0</version>
|
||||
</parent>
|
||||
<artifactId>commafeed-server</artifactId>
|
||||
<name>CommaFeed Server</name>
|
||||
|
||||
<properties>
|
||||
<quarkus.version>3.13.2</quarkus.version>
|
||||
<querydsl.version>6.6</querydsl.version>
|
||||
<quarkus.version>3.14.1</quarkus.version>
|
||||
<querydsl.version>6.7</querydsl.version>
|
||||
<rome.version>2.1.0</rome.version>
|
||||
<swagger.version>2.2.23</swagger.version>
|
||||
<properties-plugin.version>1.2.1</properties-plugin.version>
|
||||
|
||||
<build.database>h2</build.database>
|
||||
@@ -43,7 +44,7 @@
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-help-plugin</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<version>3.5.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>initialize</phase>
|
||||
@@ -78,6 +79,21 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-config-doc-maven-plugin</artifactId>
|
||||
<version>${quarkus.version}</version>
|
||||
<extensions>true</extensions>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>default-generate-asciidoc</id>
|
||||
<phase>process-test-resources</phase>
|
||||
<goals>
|
||||
<goal>generate-asciidoc</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
@@ -101,7 +117,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.4.0</version>
|
||||
<version>3.5.0</version>
|
||||
<configuration>
|
||||
<systemPropertyVariables>
|
||||
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
|
||||
@@ -111,7 +127,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<version>3.4.0</version>
|
||||
<version>3.5.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
@@ -150,7 +166,7 @@
|
||||
<plugin>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-maven-plugin-jakarta</artifactId>
|
||||
<version>2.2.22</version>
|
||||
<version>${swagger.version}</version>
|
||||
<?m2e ignore?>
|
||||
<configuration>
|
||||
<outputPath>${project.build.directory}/classes/META-INF/resources</outputPath>
|
||||
@@ -174,7 +190,14 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>3.4.0</version>
|
||||
<version>3.5.0</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.puppycrawl.tools</groupId>
|
||||
<artifactId>checkstyle</artifactId>
|
||||
<version>10.18.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>validate</id>
|
||||
@@ -228,7 +251,7 @@
|
||||
<dependency>
|
||||
<groupId>com.commafeed</groupId>
|
||||
<artifactId>commafeed-client</artifactId>
|
||||
<version>5.0.2</version>
|
||||
<version>5.1.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -311,7 +334,7 @@
|
||||
<dependency>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>2.2.22</version>
|
||||
<version>${swagger.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -330,7 +353,6 @@
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>33.3.0-jre</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
@@ -356,7 +378,7 @@
|
||||
<dependency>
|
||||
<groupId>org.passay</groupId>
|
||||
<artifactId>passay</artifactId>
|
||||
<version>1.6.4</version>
|
||||
<version>1.6.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -30,7 +30,7 @@ services:
|
||||
## Advanced
|
||||
|
||||
While using the H2 embedded database is perfectly fine for small instances, you may want to have more control over the
|
||||
database. Here's an example that uses postgresql (note image tag change from `latest-h2` to `latest-postgresql`):
|
||||
database. Here's an example that uses PostgreSQL (note the image tag change from `latest-h2` to `latest-postgresql`):
|
||||
|
||||
```
|
||||
services:
|
||||
@@ -59,6 +59,13 @@ services:
|
||||
- /path/to/commafeed/db:/var/lib/postgresql/data
|
||||
```
|
||||
|
||||
CommaFeed also supports:
|
||||
|
||||
- MySQL:
|
||||
`QUARKUS_DATASOURCE_JDBC_URL=jdbc:mysql://localhost/commafeed?autoReconnect=true&failOverReadOnly=false&maxReconnects=20&rewriteBatchedStatements=true&timezone=UTC`
|
||||
- MariaDB:
|
||||
`QUARKUS_DATASOURCE_JDBC_URL=jdbc:mariadb://localhost/commafeed?autoReconnect=true&failOverReadOnly=false&maxReconnects=20&rewriteBatchedStatements=true&timezone=UTC`
|
||||
|
||||
## Configuration
|
||||
|
||||
All [CommaFeed settings](https://github.com/Athou/commafeed/blob/master/commafeed-server/doc/commafeed.adoc) are
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.util.Optional;
|
||||
|
||||
import com.commafeed.backend.feed.FeedRefreshIntervalCalculator;
|
||||
|
||||
import io.quarkus.runtime.annotations.ConfigDocSection;
|
||||
import io.quarkus.runtime.annotations.ConfigPhase;
|
||||
import io.quarkus.runtime.annotations.ConfigRoot;
|
||||
import io.quarkus.runtime.configuration.MemorySize;
|
||||
@@ -62,26 +63,31 @@ public interface CommaFeedConfiguration {
|
||||
/**
|
||||
* HTTP client configuration
|
||||
*/
|
||||
@ConfigDocSection
|
||||
HttpClient httpClient();
|
||||
|
||||
/**
|
||||
* Feed refresh engine settings.
|
||||
*/
|
||||
@ConfigDocSection
|
||||
FeedRefresh feedRefresh();
|
||||
|
||||
/**
|
||||
* Database settings.
|
||||
*/
|
||||
@ConfigDocSection
|
||||
Database database();
|
||||
|
||||
/**
|
||||
* Users settings.
|
||||
*/
|
||||
@ConfigDocSection
|
||||
Users users();
|
||||
|
||||
/**
|
||||
* Websocket settings.
|
||||
*/
|
||||
@ConfigDocSection
|
||||
Websocket websocket();
|
||||
|
||||
interface HttpClient {
|
||||
@@ -180,7 +186,7 @@ public interface CommaFeedConfiguration {
|
||||
|
||||
interface Database {
|
||||
/**
|
||||
* Database query timeout.
|
||||
* Timeout applied to all database queries.
|
||||
*
|
||||
* 0 to disable.
|
||||
*/
|
||||
@@ -190,6 +196,7 @@ public interface CommaFeedConfiguration {
|
||||
/**
|
||||
* Database cleanup settings.
|
||||
*/
|
||||
@ConfigDocSection
|
||||
Cleanup cleanup();
|
||||
|
||||
interface Cleanup {
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package com.commafeed.backend.model;
|
||||
|
||||
import java.sql.Types;
|
||||
import java.time.Instant;
|
||||
|
||||
import org.hibernate.annotations.JdbcTypeCode;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Lob;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
@@ -18,7 +22,9 @@ public class Feed extends AbstractModel {
|
||||
/**
|
||||
* The url of the feed
|
||||
*/
|
||||
@Column(length = 2048, nullable = false)
|
||||
@Lob
|
||||
@Column(length = Integer.MAX_VALUE, nullable = false)
|
||||
@JdbcTypeCode(Types.LONGVARCHAR)
|
||||
private String url;
|
||||
|
||||
/**
|
||||
@@ -36,7 +42,9 @@ public class Feed extends AbstractModel {
|
||||
/**
|
||||
* The url of the website, extracted from the feed
|
||||
*/
|
||||
@Column(length = 2048)
|
||||
@Lob
|
||||
@Column(length = Integer.MAX_VALUE)
|
||||
@JdbcTypeCode(Types.LONGVARCHAR)
|
||||
private String link;
|
||||
|
||||
/**
|
||||
@@ -60,7 +68,9 @@ public class Feed extends AbstractModel {
|
||||
/**
|
||||
* error message while retrieving the feed
|
||||
*/
|
||||
@Column(length = 1024)
|
||||
@Lob
|
||||
@Column(length = Integer.MAX_VALUE)
|
||||
@JdbcTypeCode(Types.LONGVARCHAR)
|
||||
private String message;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package com.commafeed.backend.model;
|
||||
|
||||
import java.sql.Types;
|
||||
import java.time.Instant;
|
||||
|
||||
import org.hibernate.annotations.JdbcTypeCode;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Lob;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
@@ -21,13 +25,17 @@ public class User extends AbstractModel {
|
||||
@Column(length = 255, unique = true)
|
||||
private String email;
|
||||
|
||||
@Column(length = 256, nullable = false)
|
||||
@Lob
|
||||
@Column(length = Integer.MAX_VALUE, nullable = false)
|
||||
@JdbcTypeCode(Types.LONGVARBINARY)
|
||||
private byte[] password;
|
||||
|
||||
@Column(length = 40, unique = true)
|
||||
private String apiKey;
|
||||
|
||||
@Column(length = 8, nullable = false)
|
||||
@Lob
|
||||
@Column(length = Integer.MAX_VALUE, nullable = false)
|
||||
@JdbcTypeCode(Types.LONGVARBINARY)
|
||||
private byte[] salt;
|
||||
|
||||
@Column(nullable = false)
|
||||
|
||||
@@ -89,6 +89,8 @@ public class UserSettings extends AbstractModel {
|
||||
private boolean markAllAsReadConfirmation;
|
||||
private boolean customContextMenu;
|
||||
private boolean mobileFooter;
|
||||
private boolean unreadCountTitle;
|
||||
private boolean unreadCountFavicon;
|
||||
|
||||
private boolean email;
|
||||
private boolean gmail;
|
||||
|
||||
@@ -59,12 +59,12 @@ public class DatabaseCleaningService {
|
||||
entriesDeleted = unitOfWork.call(() -> feedEntryDAO.delete(feed.getId(), batchSize));
|
||||
entriesDeletedMeter.mark(entriesDeleted);
|
||||
entriesTotal += entriesDeleted;
|
||||
log.info("removed {} entries for feeds without subscriptions", entriesTotal);
|
||||
log.debug("removed {} entries for feeds without subscriptions", entriesTotal);
|
||||
} while (entriesDeleted > 0);
|
||||
}
|
||||
deleted = unitOfWork.call(() -> feedDAO.delete(feedDAO.findByIds(feeds.stream().map(AbstractModel::getId).toList())));
|
||||
total += deleted;
|
||||
log.info("removed {} feeds without subscriptions", total);
|
||||
log.debug("removed {} feeds without subscriptions", total);
|
||||
} while (deleted != 0);
|
||||
log.info("cleanup done: {} feeds without subscriptions deleted", total);
|
||||
}
|
||||
@@ -76,7 +76,7 @@ public class DatabaseCleaningService {
|
||||
do {
|
||||
deleted = unitOfWork.call(() -> feedEntryContentDAO.deleteWithoutEntries(batchSize));
|
||||
total += deleted;
|
||||
log.info("removed {} contents without entries", total);
|
||||
log.debug("removed {} contents without entries", total);
|
||||
} while (deleted != 0);
|
||||
log.info("cleanup done: {} contents without entries deleted", total);
|
||||
}
|
||||
@@ -98,7 +98,7 @@ public class DatabaseCleaningService {
|
||||
entriesDeletedMeter.mark(deleted);
|
||||
total += deleted;
|
||||
remaining -= deleted;
|
||||
log.info("removed {} entries for feeds exceeding capacity", total);
|
||||
log.debug("removed {} entries for feeds exceeding capacity", total);
|
||||
} while (remaining > 0);
|
||||
}
|
||||
}
|
||||
@@ -113,7 +113,7 @@ public class DatabaseCleaningService {
|
||||
deleted = unitOfWork.call(() -> feedEntryDAO.deleteEntriesOlderThan(olderThan, batchSize));
|
||||
entriesDeletedMeter.mark(deleted);
|
||||
total += deleted;
|
||||
log.info("removed {} old entries", total);
|
||||
log.debug("removed {} old entries", total);
|
||||
} while (deleted != 0);
|
||||
log.info("cleanup done: {} old entries deleted", total);
|
||||
}
|
||||
@@ -125,7 +125,7 @@ public class DatabaseCleaningService {
|
||||
do {
|
||||
deleted = unitOfWork.call(() -> feedEntryStatusDAO.deleteOldStatuses(olderThan, batchSize));
|
||||
total += deleted;
|
||||
log.info("removed {} old read statuses", total);
|
||||
log.debug("removed {} old read statuses", total);
|
||||
} while (deleted != 0);
|
||||
log.info("cleanup done: {} old read statuses deleted", total);
|
||||
}
|
||||
|
||||
@@ -70,6 +70,12 @@ public class Settings implements Serializable {
|
||||
@Schema(description = "on mobile, show action buttons at the bottom of the screen", requiredMode = RequiredMode.REQUIRED)
|
||||
private boolean mobileFooter;
|
||||
|
||||
@Schema(description = "show unread count in the title", requiredMode = RequiredMode.REQUIRED)
|
||||
private boolean unreadCountTitle;
|
||||
|
||||
@Schema(description = "show unread count in the favicon", requiredMode = RequiredMode.REQUIRED)
|
||||
private boolean unreadCountFavicon;
|
||||
|
||||
@Schema(description = "sharing settings", requiredMode = RequiredMode.REQUIRED)
|
||||
private SharingSettings sharingSettings = new SharingSettings();
|
||||
|
||||
|
||||
@@ -241,7 +241,7 @@ public class FeedREST {
|
||||
|
||||
} catch (Exception e) {
|
||||
log.debug(e.getMessage(), e);
|
||||
throw new WebApplicationException(e, Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build());
|
||||
throw new WebApplicationException(e.getMessage(), Status.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import io.swagger.v3.oas.annotations.servers.Server;
|
||||
|
||||
@OpenAPIDefinition(
|
||||
info = @Info(title = "CommaFeed API"),
|
||||
servers = { @Server(description = "CommaFeed API", url = "rest") },
|
||||
servers = { @Server(description = "CommaFeed API", url = "/") },
|
||||
security = { @SecurityRequirement(name = "basicAuth") })
|
||||
@SecurityScheme(name = "basicAuth", type = SecuritySchemeType.HTTP, scheme = "basic")
|
||||
public class OpenAPI {
|
||||
|
||||
@@ -119,6 +119,8 @@ public class UserREST {
|
||||
s.setMarkAllAsReadConfirmation(settings.isMarkAllAsReadConfirmation());
|
||||
s.setCustomContextMenu(settings.isCustomContextMenu());
|
||||
s.setMobileFooter(settings.isMobileFooter());
|
||||
s.setUnreadCountTitle(settings.isUnreadCountTitle());
|
||||
s.setUnreadCountFavicon(settings.isUnreadCountFavicon());
|
||||
} else {
|
||||
s.setReadingMode(ReadingMode.unread.name());
|
||||
s.setReadingOrder(ReadingOrder.desc.name());
|
||||
@@ -142,6 +144,8 @@ public class UserREST {
|
||||
s.setMarkAllAsReadConfirmation(true);
|
||||
s.setCustomContextMenu(true);
|
||||
s.setMobileFooter(false);
|
||||
s.setUnreadCountTitle(false);
|
||||
s.setUnreadCountFavicon(true);
|
||||
}
|
||||
return Response.ok(s).build();
|
||||
}
|
||||
@@ -173,6 +177,8 @@ public class UserREST {
|
||||
s.setMarkAllAsReadConfirmation(settings.isMarkAllAsReadConfirmation());
|
||||
s.setCustomContextMenu(settings.isCustomContextMenu());
|
||||
s.setMobileFooter(settings.isMobileFooter());
|
||||
s.setUnreadCountTitle(settings.isUnreadCountTitle());
|
||||
s.setUnreadCountFavicon(settings.isUnreadCountFavicon());
|
||||
|
||||
s.setEmail(settings.getSharingSettings().isEmail());
|
||||
s.setGmail(settings.getSharingSettings().isGmail());
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package com.commafeed.frontend.servlet;
|
||||
|
||||
import com.commafeed.backend.dao.UnitOfWork;
|
||||
import com.commafeed.backend.dao.UserSettingsDAO;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.backend.model.UserSettings;
|
||||
import com.commafeed.security.AuthenticationContext;
|
||||
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.transaction.Transactional;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
@@ -20,16 +20,16 @@ public class CustomCssServlet {
|
||||
|
||||
private final AuthenticationContext authenticationContext;
|
||||
private final UserSettingsDAO userSettingsDAO;
|
||||
private final UnitOfWork unitOfWork;
|
||||
|
||||
@GET
|
||||
@Transactional
|
||||
public String get() {
|
||||
User user = authenticationContext.getCurrentUser();
|
||||
if (user == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
UserSettings settings = unitOfWork.call(() -> userSettingsDAO.findByUser(user));
|
||||
UserSettings settings = userSettingsDAO.findByUser(user);
|
||||
if (settings == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package com.commafeed.frontend.servlet;
|
||||
|
||||
import com.commafeed.backend.dao.UnitOfWork;
|
||||
import com.commafeed.backend.dao.UserSettingsDAO;
|
||||
import com.commafeed.backend.model.User;
|
||||
import com.commafeed.backend.model.UserSettings;
|
||||
import com.commafeed.security.AuthenticationContext;
|
||||
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.transaction.Transactional;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
@@ -20,16 +20,16 @@ public class CustomJsServlet {
|
||||
|
||||
private final AuthenticationContext authenticationContext;
|
||||
private final UserSettingsDAO userSettingsDAO;
|
||||
private final UnitOfWork unitOfWork;
|
||||
|
||||
@GET
|
||||
@Transactional
|
||||
public String get() {
|
||||
User user = authenticationContext.getCurrentUser();
|
||||
if (user == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
UserSettings settings = unitOfWork.call(() -> userSettingsDAO.findByUser(user));
|
||||
UserSettings settings = userSettingsDAO.findByUser(user);
|
||||
if (settings == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import com.commafeed.backend.dao.FeedCategoryDAO;
|
||||
import com.commafeed.backend.dao.FeedEntryStatusDAO;
|
||||
import com.commafeed.backend.dao.FeedSubscriptionDAO;
|
||||
import com.commafeed.backend.dao.UnitOfWork;
|
||||
import com.commafeed.backend.model.FeedCategory;
|
||||
import com.commafeed.backend.model.FeedEntryStatus;
|
||||
import com.commafeed.backend.model.FeedSubscription;
|
||||
@@ -20,6 +19,7 @@ import com.commafeed.security.AuthenticationContext;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.transaction.Transactional;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
@@ -33,7 +33,6 @@ import lombok.RequiredArgsConstructor;
|
||||
@Singleton
|
||||
public class NextUnreadServlet {
|
||||
|
||||
private final UnitOfWork unitOfWork;
|
||||
private final FeedSubscriptionDAO feedSubscriptionDAO;
|
||||
private final FeedEntryStatusDAO feedEntryStatusDAO;
|
||||
private final FeedCategoryDAO feedCategoryDAO;
|
||||
@@ -42,36 +41,34 @@ public class NextUnreadServlet {
|
||||
private final UriInfo uri;
|
||||
|
||||
@GET
|
||||
@Transactional
|
||||
public Response get(@QueryParam("category") String categoryId, @QueryParam("order") @DefaultValue("desc") ReadingOrder order) {
|
||||
User user = authenticationContext.getCurrentUser();
|
||||
if (user == null) {
|
||||
return Response.temporaryRedirect(uri.getBaseUri()).build();
|
||||
}
|
||||
|
||||
FeedEntryStatus status = unitOfWork.call(() -> {
|
||||
FeedEntryStatus s = null;
|
||||
if (StringUtils.isBlank(categoryId) || CategoryREST.ALL.equals(categoryId)) {
|
||||
List<FeedSubscription> subs = feedSubscriptionDAO.findAll(user);
|
||||
List<FeedEntryStatus> statuses = feedEntryStatusDAO.findBySubscriptions(user, subs, true, null, null, 0, 1, order, true,
|
||||
null, null, null);
|
||||
FeedEntryStatus s = null;
|
||||
if (StringUtils.isBlank(categoryId) || CategoryREST.ALL.equals(categoryId)) {
|
||||
List<FeedSubscription> subs = feedSubscriptionDAO.findAll(user);
|
||||
List<FeedEntryStatus> statuses = feedEntryStatusDAO.findBySubscriptions(user, subs, true, null, null, 0, 1, order, true, null,
|
||||
null, null);
|
||||
s = Iterables.getFirst(statuses, null);
|
||||
} else {
|
||||
FeedCategory category = feedCategoryDAO.findById(user, Long.valueOf(categoryId));
|
||||
if (category != null) {
|
||||
List<FeedCategory> children = feedCategoryDAO.findAllChildrenCategories(user, category);
|
||||
List<FeedSubscription> subscriptions = feedSubscriptionDAO.findByCategories(user, children);
|
||||
List<FeedEntryStatus> statuses = feedEntryStatusDAO.findBySubscriptions(user, subscriptions, true, null, null, 0, 1, order,
|
||||
true, null, null, null);
|
||||
s = Iterables.getFirst(statuses, null);
|
||||
} else {
|
||||
FeedCategory category = feedCategoryDAO.findById(user, Long.valueOf(categoryId));
|
||||
if (category != null) {
|
||||
List<FeedCategory> children = feedCategoryDAO.findAllChildrenCategories(user, category);
|
||||
List<FeedSubscription> subscriptions = feedSubscriptionDAO.findByCategories(user, children);
|
||||
List<FeedEntryStatus> statuses = feedEntryStatusDAO.findBySubscriptions(user, subscriptions, true, null, null, 0, 1,
|
||||
order, true, null, null, null);
|
||||
s = Iterables.getFirst(statuses, null);
|
||||
}
|
||||
}
|
||||
if (s != null) {
|
||||
feedEntryService.markEntry(user, s.getEntry().getId(), true);
|
||||
}
|
||||
return s;
|
||||
});
|
||||
}
|
||||
if (s != null) {
|
||||
feedEntryService.markEntry(user, s.getEntry().getId(), true);
|
||||
}
|
||||
|
||||
String url = status == null ? uri.getBaseUri().toString() : status.getEntry().getUrl();
|
||||
String url = s == null ? uri.getBaseUri().toString() : s.getEntry().getUrl();
|
||||
return Response.temporaryRedirect(URI.create(url)).build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
package com.commafeed.security.mechanism;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Base64;
|
||||
|
||||
import io.quarkus.vertx.http.runtime.FormAuthConfig;
|
||||
import io.quarkus.vertx.http.runtime.FormAuthRuntimeConfig;
|
||||
import io.quarkus.vertx.http.runtime.HttpBuildTimeConfig;
|
||||
import io.quarkus.security.identity.IdentityProviderManager;
|
||||
import io.quarkus.security.identity.SecurityIdentity;
|
||||
import io.quarkus.vertx.http.runtime.HttpConfiguration;
|
||||
import io.quarkus.vertx.http.runtime.security.FormAuthenticationMechanism;
|
||||
import io.quarkus.vertx.http.runtime.security.HttpAuthenticationMechanism;
|
||||
import io.quarkus.vertx.http.runtime.security.PersistentLoginManager;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import io.vertx.core.http.Cookie;
|
||||
import io.vertx.core.http.impl.ServerCookie;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import jakarta.annotation.Priority;
|
||||
import jakarta.inject.Singleton;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.experimental.Delegate;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@@ -24,67 +21,24 @@ import lombok.extern.slf4j.Slf4j;
|
||||
* This is a workaround for https://github.com/quarkusio/quarkus/issues/42463
|
||||
*/
|
||||
@Priority(1)
|
||||
@RequiredArgsConstructor
|
||||
@Singleton
|
||||
@Slf4j
|
||||
public class CookieMaxAgeFormAuthenticationMechanism implements HttpAuthenticationMechanism {
|
||||
|
||||
// the temp encryption key, persistent across dev mode restarts
|
||||
static volatile String encryptionKey;
|
||||
|
||||
@Delegate
|
||||
private final FormAuthenticationMechanism delegate;
|
||||
private final HttpConfiguration config;
|
||||
|
||||
public CookieMaxAgeFormAuthenticationMechanism(HttpConfiguration httpConfiguration, HttpBuildTimeConfig buildTimeConfig) {
|
||||
String key;
|
||||
if (httpConfiguration.encryptionKey.isEmpty()) {
|
||||
if (encryptionKey != null) {
|
||||
// persist across dev mode restarts
|
||||
key = encryptionKey;
|
||||
} else {
|
||||
byte[] data = new byte[32];
|
||||
new SecureRandom().nextBytes(data);
|
||||
key = encryptionKey = Base64.getEncoder().encodeToString(data);
|
||||
log.warn("Encryption key was not specified for persistent FORM auth, using temporary key {}", key);
|
||||
@Override
|
||||
public Uni<SecurityIdentity> authenticate(RoutingContext context, IdentityProviderManager identityProviderManager) {
|
||||
context.addHeadersEndHandler(v -> {
|
||||
Cookie cookie = context.request().getCookie(config.auth.form.cookieName);
|
||||
if (cookie instanceof ServerCookie sc && sc.isChanged()) {
|
||||
cookie.setMaxAge(config.auth.form.timeout.toSeconds());
|
||||
}
|
||||
} else {
|
||||
key = httpConfiguration.encryptionKey.get();
|
||||
}
|
||||
});
|
||||
|
||||
FormAuthConfig form = buildTimeConfig.auth.form;
|
||||
FormAuthRuntimeConfig runtimeForm = httpConfiguration.auth.form;
|
||||
String loginPage = startWithSlash(runtimeForm.loginPage.orElse(null));
|
||||
String errorPage = startWithSlash(runtimeForm.errorPage.orElse(null));
|
||||
String landingPage = startWithSlash(runtimeForm.landingPage.orElse(null));
|
||||
String postLocation = startWithSlash(form.postLocation);
|
||||
String usernameParameter = runtimeForm.usernameParameter;
|
||||
String passwordParameter = runtimeForm.passwordParameter;
|
||||
String locationCookie = runtimeForm.locationCookie;
|
||||
String cookiePath = runtimeForm.cookiePath.orElse(null);
|
||||
boolean redirectAfterLogin = landingPage != null;
|
||||
String cookieSameSite = runtimeForm.cookieSameSite.name();
|
||||
|
||||
PersistentLoginManager loginManager = new PersistentLoginManager(key, runtimeForm.cookieName, runtimeForm.timeout.toMillis(),
|
||||
runtimeForm.newCookieInterval.toMillis(), runtimeForm.httpOnlyCookie, cookieSameSite, cookiePath) {
|
||||
@Override
|
||||
public void save(String value, RoutingContext context, String cookieName, RestoreResult restoreResult, boolean secureCookie) {
|
||||
super.save(value, context, cookieName, restoreResult, secureCookie);
|
||||
|
||||
// add max age to the cookie
|
||||
Cookie cookie = context.request().getCookie(cookieName);
|
||||
if (cookie instanceof ServerCookie sc && sc.isChanged()) {
|
||||
cookie.setMaxAge(runtimeForm.timeout.toSeconds());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.delegate = new FormAuthenticationMechanism(loginPage, postLocation, usernameParameter, passwordParameter, errorPage,
|
||||
landingPage, redirectAfterLogin, locationCookie, cookieSameSite, cookiePath, loginManager);
|
||||
}
|
||||
|
||||
private static String startWithSlash(String page) {
|
||||
if (page == null) {
|
||||
return null;
|
||||
}
|
||||
return page.startsWith("/") ? page : "/" + page;
|
||||
return delegate.authenticate(context, identityProviderManager);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
quarkus.http.port=8082
|
||||
quarkus.http.test-port=8085
|
||||
|
||||
# static files
|
||||
## make sure the webapp is always up to date
|
||||
quarkus.http.filter.index-html.header."Cache-Control"=no-cache
|
||||
quarkus.http.filter.index-html.matches=/
|
||||
## make sure the openapi documentation is always up to date
|
||||
quarkus.http.filter.openapi.header."Cache-Control"=no-cache
|
||||
quarkus.http.filter.openapi.matches=/openapi[.](json|yaml)
|
||||
|
||||
# security
|
||||
quarkus.http.auth.basic=true
|
||||
quarkus.http.auth.form.enabled=true
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?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="add-unread-count-settings" author="athou">
|
||||
<addColumn tableName="USERSETTINGS">
|
||||
<column name="unreadCountTitle" type="BOOLEAN" value="false">
|
||||
<constraints nullable="false" />
|
||||
</column>
|
||||
<column name="unreadCountFavicon" type="BOOLEAN" value="true">
|
||||
<constraints nullable="false" />
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="add-missing-fk-on-statuses-user" author="athou">
|
||||
<delete tableName="FEEDENTRYSTATUSES">
|
||||
<where>user_id not in (select id from USERS)</where>
|
||||
</delete>
|
||||
<addForeignKeyConstraint baseTableName="FEEDENTRYSTATUSES"
|
||||
baseColumnNames="user_id"
|
||||
constraintName="fk_feedentrystatuses_user"
|
||||
referencedTableName="USERS"
|
||||
referencedColumnNames="id" />
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
@@ -31,5 +31,6 @@
|
||||
<include file="changelogs/db.changelog-4.2.xml" />
|
||||
<include file="changelogs/db.changelog-4.3.xml" />
|
||||
<include file="changelogs/db.changelog-4.4.xml" />
|
||||
<include file="changelogs/db.changelog-5.1.xml" />
|
||||
|
||||
</databaseChangeLog>
|
||||
@@ -13,8 +13,8 @@ class CommaFeedConfigurationTest {
|
||||
@Test
|
||||
void verifyAsciiDocIsUpToDate() throws IOException {
|
||||
String versionedDocumentationFile = FileUtils.readFileToString(new File("doc/commafeed.adoc"), StandardCharsets.UTF_8);
|
||||
String generatedDocumentationFile = FileUtils.readFileToString(new File("../target/asciidoc/generated/config/commafeed.adoc"),
|
||||
StandardCharsets.UTF_8);
|
||||
String generatedDocumentationFile = FileUtils
|
||||
.readFileToString(new File("target/quarkus-generated-doc/config/commafeed-server.adoc"), StandardCharsets.UTF_8);
|
||||
|
||||
Assertions.assertLinesMatch(versionedDocumentationFile.lines(), generatedDocumentationFile.lines());
|
||||
}
|
||||
|
||||
@@ -4,12 +4,10 @@ import org.kohsuke.MetaInfServices;
|
||||
|
||||
import com.commafeed.backend.service.db.DatabaseStartupService;
|
||||
|
||||
import io.quarkus.liquibase.LiquibaseFactory;
|
||||
import io.quarkus.liquibase.runtime.LiquibaseSchemaProvider;
|
||||
import io.quarkus.test.junit.callback.QuarkusTestBeforeEachCallback;
|
||||
import io.quarkus.test.junit.callback.QuarkusTestMethodContext;
|
||||
import jakarta.enterprise.inject.spi.CDI;
|
||||
import liquibase.Liquibase;
|
||||
import liquibase.exception.LiquibaseException;
|
||||
|
||||
/**
|
||||
* Resets database between tests
|
||||
@@ -17,18 +15,9 @@ import liquibase.exception.LiquibaseException;
|
||||
@MetaInfServices
|
||||
public class DatabaseReset implements QuarkusTestBeforeEachCallback {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void beforeEach(QuarkusTestMethodContext context) {
|
||||
LiquibaseFactory liquibaseFactory = CDI.current().select(LiquibaseFactory.class).get();
|
||||
try (Liquibase liquibase = liquibaseFactory.createLiquibase()) {
|
||||
liquibase.dropAll();
|
||||
liquibase.update(liquibaseFactory.createContexts(), liquibaseFactory.createLabels());
|
||||
} catch (LiquibaseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
DatabaseStartupService databaseStartupService = CDI.current().select(DatabaseStartupService.class).get();
|
||||
databaseStartupService.populateInitialData();
|
||||
new LiquibaseSchemaProvider().resetAllDatabases();
|
||||
CDI.current().select(DatabaseStartupService.class).get().populateInitialData();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
package com.commafeed.backend;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.hc.client5.http.ConnectTimeoutException;
|
||||
import org.apache.hc.core5.http.HttpStatus;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
@@ -183,23 +189,6 @@ class HttpGetterTest {
|
||||
Assertions.assertEquals(2, calls.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void supportsCompression() {
|
||||
this.mockServerClient.when(HttpRequest.request().withMethod("GET")).respond(req -> {
|
||||
String acceptEncodingHeader = req.getFirstHeader(HttpHeaders.ACCEPT_ENCODING);
|
||||
if (!acceptEncodingHeader.contains("deflate")) {
|
||||
throw new Exception("deflate should be in the Accept-Encoding header");
|
||||
}
|
||||
if (!acceptEncodingHeader.contains("gzip")) {
|
||||
throw new Exception("gzip should be in the Accept-Encoding header");
|
||||
}
|
||||
|
||||
return HttpResponse.response().withBody("ok");
|
||||
});
|
||||
|
||||
Assertions.assertDoesNotThrow(() -> getter.getBinary(this.feedUrl));
|
||||
}
|
||||
|
||||
@Test
|
||||
void largeFeedWithContentLengthHeader() {
|
||||
byte[] bytes = new byte[100000];
|
||||
@@ -231,4 +220,46 @@ class HttpGetterTest {
|
||||
Assertions.assertEquals("ok", new String(result.getContent()));
|
||||
}
|
||||
|
||||
@Nested
|
||||
class Compression {
|
||||
|
||||
@Test
|
||||
void deflate() throws IOException, NotModifiedException {
|
||||
supportsCompression("deflate", DeflaterOutputStream::new);
|
||||
}
|
||||
|
||||
@Test
|
||||
void gzip() throws IOException, NotModifiedException {
|
||||
supportsCompression("gzip", GZIPOutputStream::new);
|
||||
}
|
||||
|
||||
void supportsCompression(String encoding, CompressionOutputStreamFunction compressionOutputStreamFunction)
|
||||
throws IOException, NotModifiedException {
|
||||
String body = "my body";
|
||||
|
||||
HttpGetterTest.this.mockServerClient.when(HttpRequest.request().withMethod("GET")).respond(req -> {
|
||||
String acceptEncodingHeader = req.getFirstHeader(HttpHeaders.ACCEPT_ENCODING);
|
||||
if (!Set.of(acceptEncodingHeader.split(", ")).contains(encoding)) {
|
||||
throw new Exception(encoding + " should be in the Accept-Encoding header");
|
||||
}
|
||||
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
try (OutputStream compressionOutputStream = compressionOutputStreamFunction.apply(output)) {
|
||||
compressionOutputStream.write(body.getBytes());
|
||||
}
|
||||
|
||||
return HttpResponse.response().withBody(output.toByteArray()).withHeader(HttpHeaders.CONTENT_ENCODING, encoding);
|
||||
});
|
||||
|
||||
HttpResult result = getter.getBinary(HttpGetterTest.this.feedUrl);
|
||||
Assertions.assertEquals(body, new String(result.getContent()));
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface CompressionOutputStreamFunction {
|
||||
OutputStream apply(OutputStream input) throws IOException;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.commafeed.integration;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.restassured.RestAssured;
|
||||
|
||||
@QuarkusTest
|
||||
class StaticFilesIT {
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "/", "/openapi.json", "/openapi.yaml" })
|
||||
void servedWithoutCache(String path) {
|
||||
RestAssured.given().when().get(path).then().statusCode(200).header("Cache-Control", "no-cache");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "/favicon.ico" })
|
||||
void servedWithCache(String path) {
|
||||
RestAssured.given().when().get(path).then().statusCode(200).header("Cache-Control", "public, immutable, max-age=86400");
|
||||
}
|
||||
}
|
||||
2
pom.xml
2
pom.xml
@@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>com.commafeed</groupId>
|
||||
<artifactId>commafeed</artifactId>
|
||||
<version>5.0.2</version>
|
||||
<version>5.1.0</version>
|
||||
<name>CommaFeed</name>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
|
||||
@@ -11,15 +11,23 @@
|
||||
],
|
||||
"packageRules": [
|
||||
{
|
||||
"description": "ignore our client because it's not published on maven central",
|
||||
"matchManagers": "maven",
|
||||
"matchPackagePatterns": "commafeed-client",
|
||||
"enabled": false
|
||||
},
|
||||
{
|
||||
"description": "io.quarkus.platform artifacts are released a week after io.quarkus artifacts",
|
||||
"matchManagers": "maven",
|
||||
"matchPackageNames": "io.quarkus:**",
|
||||
"enabled": false
|
||||
},
|
||||
{
|
||||
"matchManagers": "npm",
|
||||
"rangeStrategy": "bump"
|
||||
},
|
||||
{
|
||||
"description": "IBM Semeru Runtimes uses a custom versioning scheme",
|
||||
"matchDatasources": "docker",
|
||||
"matchPackageNames": "ibm-semeru-runtimes",
|
||||
"versioning": "regex:^open-(?<major>\\d+)?(\\.(?<minor>\\d+))?(\\.(?<patch>\\d+))?([\\._+](?<build>(\\d\\.?)+))?(-(?<compatibility>.*))?$",
|
||||
|
||||
Reference in New Issue
Block a user