Compare commits

..

22 Commits

Author SHA1 Message Date
Athou
2832e8c638 release 6.0.0 2026-01-10 21:01:59 +01:00
Athou
d711cbab49 requiring an email address for users is now configurable (#1914) 2026-01-10 18:11:35 +01:00
Athou
2e8fd737af remove complex password requirements (#1916) 2026-01-10 17:53:48 +01:00
Athou
a080ede15b add a setup landing page instead of creating a default admin account 2026-01-10 17:30:22 +01:00
renovate[bot]
ab3d41508f chore(deps): update dependency vite to ^7.3.1 2026-01-10 08:41:14 +00:00
Jérémie Panzer
1ab4a5e925 Merge pull request #2010 from Athou/renovate/com.ibm.icu-icu4j-78.x
fix(deps): update dependency com.ibm.icu:icu4j to v78.2
2026-01-09 05:03:28 +01:00
renovate[bot]
543ce08be6 fix(deps): update dependency com.ibm.icu:icu4j to v78.2 2026-01-08 21:59:38 +00:00
renovate[bot]
21829056ba fix(deps): update mantine monorepo to ^8.3.11 2026-01-08 05:55:36 +00:00
renovate[bot]
1af59c87d0 fix(deps): update quarkus.version to v3.30.6 2026-01-07 18:36:59 +00:00
Athou
799e6c082c cleanup 2026-01-06 22:53:37 +01:00
renovate[bot]
09635cf0fd chore(deps): update dependency @biomejs/biome to v2.3.11 2026-01-06 18:14:17 +00:00
renovate[bot]
1dfbd30471 chore(deps): update graalvm/setup-graalvm digest to 54b4f5a 2026-01-05 20:54:23 +00:00
renovate[bot]
48e0a77d1f chore(deps): lock file maintenance 2026-01-05 08:03:28 +00:00
Jérémie Panzer
7ae8594c00 Merge pull request #2009 from Athou/renovate/com.puppycrawl.tools-checkstyle-13.x
chore(deps): update dependency com.puppycrawl.tools:checkstyle to v13
2026-01-05 09:02:06 +01:00
Athou
19663b0f38 Java 25+ is now required 2026-01-05 07:25:26 +01:00
renovate[bot]
4bcb9adb83 chore(deps): update dependency com.puppycrawl.tools:checkstyle to v13 2026-01-05 01:41:55 +00:00
Athou
f7505298d7 postgresql 18+ changed the docker mount point (#2008) 2026-01-03 10:26:59 +01:00
Athou
df722ffa8b fix typo (#2007) 2026-01-02 22:00:36 +01:00
renovate[bot]
2a852fe08d fix(deps): update dependency io.github.hakky54:ayza-for-apache5 to v10.0.3 2026-01-02 09:36:08 +00:00
Jérémie Panzer
540f796200 Merge pull request #2006 from Athou/renovate/org.jsoup-jsoup-1.x
fix(deps): update dependency org.jsoup:jsoup to v1.22.1
2026-01-01 08:42:47 +01:00
renovate[bot]
b726ac84fe fix(deps): update dependency org.jsoup:jsoup to v1.22.1 2026-01-01 07:02:22 +00:00
renovate[bot]
61ac2bb6a3 chore(deps): update dependency com.puppycrawl.tools:checkstyle to v12.3.1 2025-12-31 16:00:34 +00:00
89 changed files with 1389 additions and 379 deletions

View File

@@ -29,7 +29,7 @@ jobs:
# Setup
- name: Set up GraalVM
uses: graalvm/setup-graalvm@790e28947b79a9c09c3391c0f18bf8d0f102ed69 # v1
uses: graalvm/setup-graalvm@54b4f5a65c1a84b2fdfdc2078fe43df32819e4b1 # v1
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: "graalvm"

View File

@@ -1,5 +1,12 @@
# Changelog
## [6.0.0]
- When booting CommaFeed for the first time, the default "admin" account is no longer created automatically. A setup wizard will guide you through the creation of an admin account
- Default password complexity requirements have been lowered for local network deployments, where strict password rules are often unnecessary. The `commafeed.users.strict-password-policy` setting has been replaced by `commafeed.users.minimum-password-length` with a default value of `4` (#1916)
- Email addresses are no longer required when creating users and when they update their profile. The `commafeed.users.email-address-required` setting has been added to restore the previous behavior (#1914)
- Java 25+ is now required to build and run CommaFeed
## [5.12.1]
- The favicon is now crispier (#1978)

View File

@@ -120,7 +120,6 @@ meaning that you will have to log back in after each restart of the application.
All other Quarkus settings can be found [here](https://quarkus.io/guides/all-config).
When started, the server will listen on http://localhost:8082.
The default user is `admin` and the default password is `admin`.
### Updates

View File

@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/2.3.10/schema.json",
"$schema": "https://biomejs.dev/schemas/2.3.11/schema.json",
"formatter": {
"indentStyle": "space",
"indentWidth": 4,

View File

@@ -12,12 +12,12 @@
"@fontsource/open-sans": "^5.2.7",
"@lingui/core": "^5.7.0",
"@lingui/react": "^5.7.0",
"@mantine/core": "^8.3.10",
"@mantine/form": "^8.3.10",
"@mantine/hooks": "^8.3.10",
"@mantine/modals": "^8.3.10",
"@mantine/notifications": "^8.3.10",
"@mantine/spotlight": "^8.3.10",
"@mantine/core": "^8.3.11",
"@mantine/form": "^8.3.11",
"@mantine/hooks": "^8.3.11",
"@mantine/modals": "^8.3.11",
"@mantine/notifications": "^8.3.11",
"@mantine/spotlight": "^8.3.11",
"@monaco-editor/react": "^4.7.0",
"@reduxjs/toolkit": "^2.11.2",
"axios": "^1.13.2",
@@ -43,7 +43,7 @@
"websocket-heartbeat-js": "^1.1.3"
},
"devDependencies": {
"@biomejs/biome": "^2.3.10",
"@biomejs/biome": "^2.3.11",
"@lingui/babel-plugin-lingui-macro": "^5.7.0",
"@lingui/cli": "^5.7.0",
"@lingui/vite-plugin": "^5.7.0",
@@ -60,7 +60,7 @@
"babel-plugin-react-compiler": "1.0.0",
"jsdom": "^27.4.0",
"typescript": "^5.9.3",
"vite": "^7.3.0",
"vite": "^7.3.1",
"vite-plugin-checker": "^0.12.0",
"vite-tsconfig-paths": "^6.0.3",
"vitest": "^4.0.16",
@@ -426,9 +426,9 @@
}
},
"node_modules/@biomejs/biome": {
"version": "2.3.10",
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.3.10.tgz",
"integrity": "sha512-/uWSUd1MHX2fjqNLHNL6zLYWBbrJeG412/8H7ESuK8ewoRoMPUgHDebqKrPTx/5n6f17Xzqc9hdg3MEqA5hXnQ==",
"version": "2.3.11",
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.3.11.tgz",
"integrity": "sha512-/zt+6qazBWguPG6+eWmiELqO+9jRsMZ/DBU3lfuU2ngtIQYzymocHhKiZRyrbra4aCOoyTg/BmY+6WH5mv9xmQ==",
"dev": true,
"license": "MIT OR Apache-2.0",
"peer": true,
@@ -443,20 +443,20 @@
"url": "https://opencollective.com/biome"
},
"optionalDependencies": {
"@biomejs/cli-darwin-arm64": "2.3.10",
"@biomejs/cli-darwin-x64": "2.3.10",
"@biomejs/cli-linux-arm64": "2.3.10",
"@biomejs/cli-linux-arm64-musl": "2.3.10",
"@biomejs/cli-linux-x64": "2.3.10",
"@biomejs/cli-linux-x64-musl": "2.3.10",
"@biomejs/cli-win32-arm64": "2.3.10",
"@biomejs/cli-win32-x64": "2.3.10"
"@biomejs/cli-darwin-arm64": "2.3.11",
"@biomejs/cli-darwin-x64": "2.3.11",
"@biomejs/cli-linux-arm64": "2.3.11",
"@biomejs/cli-linux-arm64-musl": "2.3.11",
"@biomejs/cli-linux-x64": "2.3.11",
"@biomejs/cli-linux-x64-musl": "2.3.11",
"@biomejs/cli-win32-arm64": "2.3.11",
"@biomejs/cli-win32-x64": "2.3.11"
}
},
"node_modules/@biomejs/cli-darwin-arm64": {
"version": "2.3.10",
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.3.10.tgz",
"integrity": "sha512-M6xUjtCVnNGFfK7HMNKa593nb7fwNm43fq1Mt71kpLpb+4mE7odO8W/oWVDyBVO4ackhresy1ZYO7OJcVo/B7w==",
"version": "2.3.11",
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.3.11.tgz",
"integrity": "sha512-/uXXkBcPKVQY7rc9Ys2CrlirBJYbpESEDme7RKiBD6MmqR2w3j0+ZZXRIL2xiaNPsIMMNhP1YnA+jRRxoOAFrA==",
"cpu": [
"arm64"
],
@@ -471,9 +471,9 @@
}
},
"node_modules/@biomejs/cli-darwin-x64": {
"version": "2.3.10",
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.3.10.tgz",
"integrity": "sha512-Vae7+V6t/Avr8tVbFNjnFSTKZogZHFYl7MMH62P/J1kZtr0tyRQ9Fe0onjqjS2Ek9lmNLmZc/VR5uSekh+p1fg==",
"version": "2.3.11",
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.3.11.tgz",
"integrity": "sha512-fh7nnvbweDPm2xEmFjfmq7zSUiox88plgdHF9OIW4i99WnXrAC3o2P3ag9judoUMv8FCSUnlwJCM1B64nO5Fbg==",
"cpu": [
"x64"
],
@@ -488,9 +488,9 @@
}
},
"node_modules/@biomejs/cli-linux-arm64": {
"version": "2.3.10",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.3.10.tgz",
"integrity": "sha512-hhPw2V3/EpHKsileVOFynuWiKRgFEV48cLe0eA+G2wO4SzlwEhLEB9LhlSrVeu2mtSn205W283LkX7Fh48CaxA==",
"version": "2.3.11",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.3.11.tgz",
"integrity": "sha512-l4xkGa9E7Uc0/05qU2lMYfN1H+fzzkHgaJoy98wO+b/7Gl78srbCRRgwYSW+BTLixTBrM6Ede5NSBwt7rd/i6g==",
"cpu": [
"arm64"
],
@@ -505,9 +505,9 @@
}
},
"node_modules/@biomejs/cli-linux-arm64-musl": {
"version": "2.3.10",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.3.10.tgz",
"integrity": "sha512-B9DszIHkuKtOH2IFeeVkQmSMVUjss9KtHaNXquYYWCjH8IstNgXgx5B0aSBQNr6mn4RcKKRQZXn9Zu1rM3O0/A==",
"version": "2.3.11",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.3.11.tgz",
"integrity": "sha512-XPSQ+XIPZMLaZ6zveQdwNjbX+QdROEd1zPgMwD47zvHV+tCGB88VH+aynyGxAHdzL+Tm/+DtKST5SECs4iwCLg==",
"cpu": [
"arm64"
],
@@ -522,9 +522,9 @@
}
},
"node_modules/@biomejs/cli-linux-x64": {
"version": "2.3.10",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.3.10.tgz",
"integrity": "sha512-wwAkWD1MR95u+J4LkWP74/vGz+tRrIQvr8kfMMJY8KOQ8+HMVleREOcPYsQX82S7uueco60L58Wc6M1I9WA9Dw==",
"version": "2.3.11",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.3.11.tgz",
"integrity": "sha512-/1s9V/H3cSe0r0Mv/Z8JryF5x9ywRxywomqZVLHAoa/uN0eY7F8gEngWKNS5vbbN/BsfpCG5yeBT5ENh50Frxg==",
"cpu": [
"x64"
],
@@ -539,9 +539,9 @@
}
},
"node_modules/@biomejs/cli-linux-x64-musl": {
"version": "2.3.10",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.3.10.tgz",
"integrity": "sha512-QTfHZQh62SDFdYc2nfmZFuTm5yYb4eO1zwfB+90YxUumRCR171tS1GoTX5OD0wrv4UsziMPmrePMtkTnNyYG3g==",
"version": "2.3.11",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.3.11.tgz",
"integrity": "sha512-vU7a8wLs5C9yJ4CB8a44r12aXYb8yYgBn+WeyzbMjaCMklzCv1oXr8x+VEyWodgJt9bDmhiaW/I0RHbn7rsNmw==",
"cpu": [
"x64"
],
@@ -556,9 +556,9 @@
}
},
"node_modules/@biomejs/cli-win32-arm64": {
"version": "2.3.10",
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.3.10.tgz",
"integrity": "sha512-o7lYc9n+CfRbHvkjPhm8s9FgbKdYZu5HCcGVMItLjz93EhgJ8AM44W+QckDqLA9MKDNFrR8nPbO4b73VC5kGGQ==",
"version": "2.3.11",
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.3.11.tgz",
"integrity": "sha512-PZQ6ElCOnkYapSsysiTy0+fYX+agXPlWugh6+eQ6uPKI3vKAqNp6TnMhoM3oY2NltSB89hz59o8xIfOdyhi9Iw==",
"cpu": [
"arm64"
],
@@ -573,9 +573,9 @@
}
},
"node_modules/@biomejs/cli-win32-x64": {
"version": "2.3.10",
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.3.10.tgz",
"integrity": "sha512-pHEFgq7dUEsKnqG9mx9bXihxGI49X+ar+UBrEIj3Wqj3UCZp1rNgV+OoyjFgcXsjCWpuEAF4VJdkZr3TrWdCbQ==",
"version": "2.3.11",
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.3.11.tgz",
"integrity": "sha512-43VrG813EW+b5+YbDbz31uUsheX+qFKCpXeY9kfdAx+ww3naKxeVkTD9zLIWxUPfJquANMHrmW3wbe/037G0Qg==",
"cpu": [
"x64"
],
@@ -1296,9 +1296,9 @@
}
},
"node_modules/@exodus/bytes": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.7.0.tgz",
"integrity": "sha512-5i+BtvujK/vM07YCGDyz4C4AyDzLmhxHMtM5HpUyPRtJPBdFPsj290ffXW+UXY21/G7GtXeHD2nRmq0T1ShyQQ==",
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.8.0.tgz",
"integrity": "sha512-8JPn18Bcp8Uo1T82gR8lh2guEOa5KKU/IEKvvdp0sgmi7coPBWf1Doi1EXsGZb2ehc8ym/StJCjffYV+ne7sXQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1704,9 +1704,9 @@
}
},
"node_modules/@mantine/core": {
"version": "8.3.10",
"resolved": "https://registry.npmjs.org/@mantine/core/-/core-8.3.10.tgz",
"integrity": "sha512-aKQFETN14v6GtM07b/G5yJneMM1yrgf9mNrTah6GVy5DvQM0AeutITT7toHqh5gxxwzdg/DoY+HQsv5zhqnc5g==",
"version": "8.3.11",
"resolved": "https://registry.npmjs.org/@mantine/core/-/core-8.3.11.tgz",
"integrity": "sha512-FWXp94tiTFdh8BKc7UTNdIuumD96JFxLF/VvUWGRzf5i563Ye0Ma/aZtOBwpdEtdIQkojOhoMMV76k5toOQgEQ==",
"license": "MIT",
"peer": true,
"dependencies": {
@@ -1718,15 +1718,15 @@
"type-fest": "^4.41.0"
},
"peerDependencies": {
"@mantine/hooks": "8.3.10",
"@mantine/hooks": "8.3.11",
"react": "^18.x || ^19.x",
"react-dom": "^18.x || ^19.x"
}
},
"node_modules/@mantine/form": {
"version": "8.3.10",
"resolved": "https://registry.npmjs.org/@mantine/form/-/form-8.3.10.tgz",
"integrity": "sha512-TuBmCUIH0qHUig+y9My3bLL9CRoW4g9bijIF6743gqVh0o/daSwplc2TTVMj6sl+F1MR+SJiHtAC8FoR7fdhNw==",
"version": "8.3.11",
"resolved": "https://registry.npmjs.org/@mantine/form/-/form-8.3.11.tgz",
"integrity": "sha512-546npW8gHCXUShVL/OOnKBJ1So5bcKTdmiNxzXn8q2eGhMIB+zdj7/v4QUz2j55OjDRPq0dAVJhzDjLfzeibTg==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
@@ -1737,9 +1737,9 @@
}
},
"node_modules/@mantine/hooks": {
"version": "8.3.10",
"resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-8.3.10.tgz",
"integrity": "sha512-bv+yYHl+keTIvakiDzVJMIjW+o8/Px0G3EdpCMFG+U2ux6SwQqluqoq+/kqrTtT6RaLvQ0fMxjpIULF2cu/xAg==",
"version": "8.3.11",
"resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-8.3.11.tgz",
"integrity": "sha512-WJFKDnJJM2fAOCkrO8qRsi3wDdicSFqElruPj5TEYWDL93vl0pITOyN0oRF93bUtx190waqela3edfGH3r5/1g==",
"license": "MIT",
"peer": true,
"peerDependencies": {
@@ -1747,52 +1747,52 @@
}
},
"node_modules/@mantine/modals": {
"version": "8.3.10",
"resolved": "https://registry.npmjs.org/@mantine/modals/-/modals-8.3.10.tgz",
"integrity": "sha512-XopCrP8dindhzSDazU47BgU8TVsiOyEG0u1UMJJ4u8TdvBctP7QVeJmGKj+B4MRHk2cHrjIF38dEGJhDgTITEg==",
"version": "8.3.11",
"resolved": "https://registry.npmjs.org/@mantine/modals/-/modals-8.3.11.tgz",
"integrity": "sha512-LFLpIanPWBMmz3OTunYKzdL+t2p3iu2LiEPlvfVEW9bD45dsMcuUPX4cinCW/E1/qT7X1rhTIdbQuOTUtdw4BQ==",
"license": "MIT",
"peerDependencies": {
"@mantine/core": "8.3.10",
"@mantine/hooks": "8.3.10",
"@mantine/core": "8.3.11",
"@mantine/hooks": "8.3.11",
"react": "^18.x || ^19.x",
"react-dom": "^18.x || ^19.x"
}
},
"node_modules/@mantine/notifications": {
"version": "8.3.10",
"resolved": "https://registry.npmjs.org/@mantine/notifications/-/notifications-8.3.10.tgz",
"integrity": "sha512-0aVpRCyn9u0wuryBnFu1jOwBYw6xGeaNNtTcTUnSvkL6NAypfPon6JG7Wsekf3IuWSTLBjhYaFEIEd4nh7VDpg==",
"version": "8.3.11",
"resolved": "https://registry.npmjs.org/@mantine/notifications/-/notifications-8.3.11.tgz",
"integrity": "sha512-bcFzw9m4uzxHMflNeWqa9imCy8T5mHCAV8WRqzAwK/Yls8GL9uJoQH/e1v6Pl4g4yvBmaF9yUHu7Zq3iyKhHhQ==",
"license": "MIT",
"dependencies": {
"@mantine/store": "8.3.10",
"@mantine/store": "8.3.11",
"react-transition-group": "4.4.5"
},
"peerDependencies": {
"@mantine/core": "8.3.10",
"@mantine/hooks": "8.3.10",
"@mantine/core": "8.3.11",
"@mantine/hooks": "8.3.11",
"react": "^18.x || ^19.x",
"react-dom": "^18.x || ^19.x"
}
},
"node_modules/@mantine/spotlight": {
"version": "8.3.10",
"resolved": "https://registry.npmjs.org/@mantine/spotlight/-/spotlight-8.3.10.tgz",
"integrity": "sha512-0GfQd/smRcd5u0o6Ad7J9ZEWLcZZ81h9/Z9qUnzIlJeYjXqJdr40MMqDxNsXgZEDKscPJkggZMqMiRZXhFbdNQ==",
"version": "8.3.11",
"resolved": "https://registry.npmjs.org/@mantine/spotlight/-/spotlight-8.3.11.tgz",
"integrity": "sha512-ddtCs33NUUMX8QOuqKi2TfmCNJQiwvdl/o90prfFQrOmcy7KiWuhl5vL7HFEuY8von8G6To7wQGMH9UdrvY6jQ==",
"license": "MIT",
"dependencies": {
"@mantine/store": "8.3.10"
"@mantine/store": "8.3.11"
},
"peerDependencies": {
"@mantine/core": "8.3.10",
"@mantine/hooks": "8.3.10",
"@mantine/core": "8.3.11",
"@mantine/hooks": "8.3.11",
"react": "^18.x || ^19.x",
"react-dom": "^18.x || ^19.x"
}
},
"node_modules/@mantine/store": {
"version": "8.3.10",
"resolved": "https://registry.npmjs.org/@mantine/store/-/store-8.3.10.tgz",
"integrity": "sha512-38t1UivcucZo9hQq27F/eqR5GvovNs4NHEz6DchOuZzV5IJWqO8+T07ivb8wct47ovYe42rPfLcaOdnIEvMsJA==",
"version": "8.3.11",
"resolved": "https://registry.npmjs.org/@mantine/store/-/store-8.3.11.tgz",
"integrity": "sha512-ku48CzN9HobVUjd7o41voaaHKz2CMrtkk454vwXWAspfbpqR4hklL5zq/MzctETFCG6HBDiPRfZAvNm8WaM3Tw==",
"license": "MIT",
"peerDependencies": {
"react": "^18.x || ^19.x"
@@ -2934,9 +2934,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001761",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001761.tgz",
"integrity": "sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==",
"version": "1.0.30001762",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001762.tgz",
"integrity": "sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==",
"devOptional": true,
"funding": [
{
@@ -3195,20 +3195,31 @@
"license": "MIT"
},
"node_modules/cssstyle": {
"version": "5.3.5",
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.5.tgz",
"integrity": "sha512-GlsEptulso7Jg0VaOZ8BXQi3AkYM5BOJKEO/rjMidSCq70FkIC5y0eawrCXeYzxgt3OCf4Ls+eoxN+/05vN0Ag==",
"version": "5.3.6",
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.6.tgz",
"integrity": "sha512-legscpSpgSAeGEe0TNcai97DKt9Vd9AsAdOL7Uoetb52Ar/8eJm3LIa39qpv8wWzLFlNG4vVvppQM+teaMPj3A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@asamuzakjp/css-color": "^4.1.1",
"@csstools/css-syntax-patches-for-csstree": "^1.0.21",
"css-tree": "^3.1.0"
"css-tree": "^3.1.0",
"lru-cache": "^11.2.4"
},
"engines": {
"node": ">=20"
}
},
"node_modules/cssstyle/node_modules/lru-cache": {
"version": "11.2.4",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz",
"integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==",
"dev": true,
"license": "BlueOak-1.0.0",
"engines": {
"node": "20 || >=22"
}
},
"node_modules/csstype": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
@@ -3879,9 +3890,9 @@
"license": "BSD-3-Clause"
},
"node_modules/immer": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/immer/-/immer-11.1.0.tgz",
"integrity": "sha512-dlzb07f5LDY+tzs+iLCSXV2yuhaYfezqyZQc+n6baLECWkOMEWxkECAOnXL0ba7lsA25fM9b2jtzpu/uxo1a7g==",
"version": "11.1.3",
"resolved": "https://registry.npmjs.org/immer/-/immer-11.1.3.tgz",
"integrity": "sha512-6jQTc5z0KJFtr1UgFpIL3N9XSC3saRaI9PwWtzM2pSqkNGtiNkYY2OSwkOGDK2XcTRcLb1pi/aNkKZz0nxVH4Q==",
"license": "MIT",
"funding": {
"type": "opencollective",
@@ -5630,9 +5641,9 @@
"license": "MIT"
},
"node_modules/tabbable": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.3.0.tgz",
"integrity": "sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==",
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz",
"integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==",
"license": "MIT"
},
"node_modules/threads": {
@@ -6058,9 +6069,9 @@
"license": "MIT"
},
"node_modules/vite": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.0.tgz",
"integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -6899,9 +6910,9 @@
}
},
"node_modules/webidl-conversions": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.0.tgz",
"integrity": "sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA==",
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz",
"integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {

View File

@@ -19,12 +19,12 @@
"@fontsource/open-sans": "^5.2.7",
"@lingui/core": "^5.7.0",
"@lingui/react": "^5.7.0",
"@mantine/core": "^8.3.10",
"@mantine/form": "^8.3.10",
"@mantine/hooks": "^8.3.10",
"@mantine/modals": "^8.3.10",
"@mantine/notifications": "^8.3.10",
"@mantine/spotlight": "^8.3.10",
"@mantine/core": "^8.3.11",
"@mantine/form": "^8.3.11",
"@mantine/hooks": "^8.3.11",
"@mantine/modals": "^8.3.11",
"@mantine/notifications": "^8.3.11",
"@mantine/spotlight": "^8.3.11",
"@monaco-editor/react": "^4.7.0",
"@reduxjs/toolkit": "^2.11.2",
"axios": "^1.13.2",
@@ -50,7 +50,7 @@
"websocket-heartbeat-js": "^1.1.3"
},
"devDependencies": {
"@biomejs/biome": "^2.3.10",
"@biomejs/biome": "^2.3.11",
"@lingui/babel-plugin-lingui-macro": "^5.7.0",
"@lingui/cli": "^5.7.0",
"@lingui/vite-plugin": "^5.7.0",
@@ -67,7 +67,7 @@
"babel-plugin-react-compiler": "1.0.0",
"jsdom": "^27.4.0",
"typescript": "^5.9.3",
"vite": "^7.3.0",
"vite": "^7.3.1",
"vite-plugin-checker": "^0.12.0",
"vite-tsconfig-paths": "^6.0.3",
"vitest": "^4.0.16",

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>com.commafeed</groupId>
<artifactId>commafeed</artifactId>
<version>5.12.1</version>
<version>6.0.0</version>
</parent>
<artifactId>commafeed-client</artifactId>
<name>CommaFeed Client</name>

View File

@@ -9,6 +9,7 @@ import { HashRouter, Navigate, Route, Routes, useNavigate } from "react-router-d
import Tinycon from "tinycon"
import { Constants } from "@/app/constants"
import { redirectTo } from "@/app/redirect/slice"
import { redirectToInitialSetup } from "@/app/redirect/thunks"
import { reloadServerInfos } from "@/app/server/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import { categoryUnreadCount } from "@/app/utils"
@@ -30,6 +31,7 @@ import { FeedEntriesPage } from "@/pages/app/FeedEntriesPage"
import Layout from "@/pages/app/Layout"
import { SettingsPage } from "@/pages/app/SettingsPage"
import { TagDetailsPage } from "@/pages/app/TagDetailsPage"
import { InitialSetupPage } from "@/pages/auth/InitialSetupPage"
import { LoginPage } from "@/pages/auth/LoginPage"
import { PasswordRecoveryPage } from "@/pages/auth/PasswordRecoveryPage"
import { RegistrationPage } from "@/pages/auth/RegistrationPage"
@@ -82,6 +84,7 @@ function AppRoutes() {
<Routes>
<Route path="/" element={<Navigate to={`/app/category/${Constants.categories.all.id}`} replace />} />
<Route path="welcome" element={<WelcomePage />} />
<Route path="setup" element={<InitialSetupPage />} />
<Route path="login" element={<LoginPage />} />
<Route path="register" element={<RegistrationPage />} />
<Route path="passwordRecovery" element={<PasswordRecoveryPage />} />
@@ -112,6 +115,18 @@ function AppRoutes() {
)
}
function InitialSetupHandler() {
const serverInfos = useAppSelector(state => state.server.serverInfos)
const dispatch = useAppDispatch()
useEffect(() => {
if (serverInfos?.initialSetupRequired) {
dispatch(redirectToInitialSetup())
}
}, [serverInfos, dispatch])
return null
}
function RedirectHandler() {
const target = useAppSelector(state => state.redirect.to)
const dispatch = useAppDispatch()
@@ -216,6 +231,7 @@ export function App() {
<DisablePullToRefresh enabled={disablePullToRefresh} />
<HashRouter>
<InitialSetupHandler />
<RedirectHandler />
<AppRoutes />
</HashRouter>

View File

@@ -12,6 +12,7 @@ import type {
FeedModificationRequest,
GetEntriesPaginatedRequest,
IDRequest,
InitialSetupRequest,
LoginRequest,
MarkRequest,
Metrics,
@@ -32,16 +33,17 @@ const axiosInstance = axios.create({ baseURL: "./rest", withCredentials: true })
axiosInstance.interceptors.response.use(
response => response,
error => {
if (isAuthenticationError(error)) {
if (isAuthenticationError(error) && window.location.hash !== "#/login") {
const data = error.response?.data
window.location.hash = data?.allowRegistrations ? "/welcome" : "/login"
window.location.reload()
}
throw error
}
)
function isAuthenticationError(error: unknown): error is AxiosError<AuthenticationError> {
return axios.isAxiosError(error) && !!error.response && [401, 403].includes(error.response.status)
return axios.isAxiosError(error) && error.response?.status === 401
}
export const client = {
@@ -93,6 +95,7 @@ export const client = {
})
},
register: async (req: RegistrationRequest) => await axiosInstance.post("user/register", req),
initialSetup: async (req: InitialSetupRequest) => await axiosInstance.post("user/initialSetup", req),
passwordReset: async (req: PasswordResetRequest) => await axiosInstance.post("user/passwordReset", req),
getSettings: async () => await axiosInstance.get<Settings>("user/settings"),
saveSettings: async (settings: Settings) => await axiosInstance.post("user/settings", settings),

View File

@@ -6,6 +6,8 @@ export const redirectToLogin = createAppAsyncThunk("redirect/login", (_, thunkAp
export const redirectToRegistration = createAppAsyncThunk("redirect/register", (_, thunkApi) => thunkApi.dispatch(redirectTo("/register")))
export const redirectToInitialSetup = createAppAsyncThunk("redirect/initialSetup", (_, thunkApi) => thunkApi.dispatch(redirectTo("/setup")))
export const redirectToApiDocumentation = createAppAsyncThunk("redirect/api", () => {
window.location.href = "api-documentation/"
})

View File

@@ -209,17 +209,25 @@ export interface RegistrationRequest {
email: string
}
export interface InitialSetupRequest {
name: string
password: string
email?: string
}
export interface ServerInfo {
announcement?: string
version: string
gitCommit: string
allowRegistrations: boolean
emailAddressRequired: boolean
smtpEnabled: boolean
demoAccountEnabled: boolean
websocketEnabled: boolean
websocketPingInterval: number
treeReloadInterval: number
forceRefreshCooldownDuration: number
initialSetupRequired: boolean
}
export interface SharingSettings {

View File

@@ -20,6 +20,7 @@ interface FormData extends ProfileModificationRequest {
export function ProfileSettings() {
const profile = useAppSelector(state => state.user.profile)
const serverInfos = useAppSelector(state => state.server.serverInfos)
const dispatch = useAppDispatch()
const { _ } = useLingui()
@@ -134,7 +135,12 @@ export function ProfileSettings() {
required
{...form.getInputProps("currentPassword")}
/>
<TextInput type="email" label={<Trans>E-mail</Trans>} {...form.getInputProps("email")} required />
<TextInput
type="email"
label={<Trans>E-mail</Trans>}
{...form.getInputProps("email")}
required={serverInfos?.emailAddressRequired}
/>
<PasswordInput
label={<Trans>New password</Trans>}
description={<Trans>Changing password will generate a new API key</Trans>}

View File

@@ -60,6 +60,11 @@ msgstr "إضافة مستخدم"
msgid "Admin"
msgstr "إداري"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,12 +142,12 @@ msgstr "العودة لتسجيل الدخول"
msgid "Blue"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr ""
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
@@ -225,6 +230,10 @@ msgstr "تأكيد كلمة المرور"
msgid "Cozy"
msgstr "دافئ"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr "السيطرة"
@@ -309,6 +318,8 @@ msgstr "اسحب الرابط إلى شريط الإشارات"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "في العرض الموسع ، التمرير عبر الإدخالات
msgid "Indigo"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "الفئة الأصل"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr "إلغاء النجم"
msgid "Unsubscribe"
msgstr "إلغاء الاشتراك"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "اسم المستخدم"
@@ -1060,6 +1081,10 @@ msgstr "تحذير"
msgid "Website"
msgstr "موقع الكتروني"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr ""

View File

@@ -60,6 +60,11 @@ msgstr "Afegeix usuari"
msgid "Admin"
msgstr "Administrador"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,14 +142,14 @@ msgstr "Torna a iniciar sessió"
msgid "Blue"
msgstr "Blau"
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr "Extensió del navegador"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr "Extensió del navegador necessària per a Chrome"
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
msgstr "Extensió del navegador"
#: src/components/settings/DisplaySettings.tsx
msgid "Browser tab"
msgstr "Pestanya del navegador"
@@ -225,6 +230,10 @@ msgstr "Confirmeu la contrasenya"
msgid "Cozy"
msgstr "Acollidor"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr "Ctrl"
@@ -285,7 +294,7 @@ msgstr "Detallat"
#: src/components/settings/DisplaySettings.tsx
msgid "Disable \"Pull to refresh\" browser behavior"
msgstr "Desactiva el comportament \"Arrossega per actualitzar"\ del navegador"
msgstr "Desactiva el comportament \"Arrossega per actualitzar\"\\ del navegador"
#: src/components/header/ProfileMenu.tsx
#: src/components/settings/DisplaySettings.tsx
@@ -309,6 +318,8 @@ msgstr "Arrossegueu l'enllaç a la barra d'adreces d'interès"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "En la vista ampliada, en desplaçar-se per les entrades, es marquen com
msgid "Indigo"
msgstr "Indi"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "Categoria pare"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr "Desestrellar"
msgid "Unsubscribe"
msgstr "Donar-se de baixa"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "Nom d'usuari"
@@ -1060,6 +1081,10 @@ msgstr "Avís"
msgid "Website"
msgstr "Lloc web"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr "Groc"

View File

@@ -60,6 +60,11 @@ msgstr "Přidat uživatele"
msgid "Admin"
msgstr "Správce"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,12 +142,12 @@ msgstr "Zpět k přihlášení"
msgid "Blue"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr ""
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
@@ -225,6 +230,10 @@ msgstr "Potvrďte heslo"
msgid "Cozy"
msgstr "Útulný"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr ""
@@ -309,6 +318,8 @@ msgstr "Přetáhněte odkaz na lištu záložek"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "V rozšířeném zobrazení je procházením označíte jako přečtené
msgid "Indigo"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "Rodičovská kategorie"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr "Odstranit hvězdu"
msgid "Unsubscribe"
msgstr "Odhlásit odběr"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "Uživatelské jméno"
@@ -1060,6 +1081,10 @@ msgstr "Varování"
msgid "Website"
msgstr "Webové stránky"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr ""

View File

@@ -60,6 +60,11 @@ msgstr "Ychwanegu defnyddiwr"
msgid "Admin"
msgstr "Gweinyddol"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,12 +142,12 @@ msgstr "Yn ôl i fewngofnodi"
msgid "Blue"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr ""
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
@@ -225,6 +230,10 @@ msgstr "Cadarnhau'r cyfrinair"
msgid "Cozy"
msgstr "clyd"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr ""
@@ -309,6 +318,8 @@ msgstr "Llusgwch y ddolen i'r bar nod tudalen"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "Mewn gwedd estynedig, mae sgrolio trwy gofnodion yn nodi eu bod wedi'u d
msgid "Indigo"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "Categori Rhiant"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr "dad-seren"
msgid "Unsubscribe"
msgstr "Dad-danysgrifio"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "Enw defnyddiwr"
@@ -1060,6 +1081,10 @@ msgstr "Rhybudd"
msgid "Website"
msgstr "Gwefan"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr ""

View File

@@ -60,6 +60,11 @@ msgstr "Tilføj bruger"
msgid "Admin"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,12 +142,12 @@ msgstr "Tilbage for at logge ind"
msgid "Blue"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr ""
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
@@ -225,6 +230,10 @@ msgstr "Bekræft adgangskode"
msgid "Cozy"
msgstr "Hyggeligt"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr ""
@@ -309,6 +318,8 @@ msgstr "Træk linket til bogmærkelinjen"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "I udvidet visning markerer du dem som læst, når du ruller gennem poste
msgid "Indigo"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "Forældrekategori"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr ""
msgid "Unsubscribe"
msgstr "Afmeld"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "Brugernavn"
@@ -1060,6 +1081,10 @@ msgstr "Advarsel"
msgid "Website"
msgstr "Hjemmeside"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr ""

View File

@@ -60,6 +60,11 @@ msgstr "Benutzer hinzufügen"
msgid "Admin"
msgstr "Verwaltung"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,14 +142,14 @@ msgstr "Zurück zum Login"
msgid "Blue"
msgstr ""
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr "Browser-Erweiterung"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr "Browser-Erweiterung für Chrome benötigt"
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
msgstr "Browser-Erweiterung"
#: src/components/settings/DisplaySettings.tsx
msgid "Browser tab"
msgstr ""
@@ -225,6 +230,10 @@ msgstr "Passwort bestätigen"
msgid "Cozy"
msgstr "Gemütlich"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr "Strg"
@@ -309,6 +318,8 @@ msgstr "Link in Lesezeichenleiste ziehen"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "In der erweiterten Ansicht werden Einträge beim Scrollen als gelesen ma
msgid "Indigo"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "Übergeordnete Kategorie"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr "Stern entfernen"
msgid "Unsubscribe"
msgstr "Abbestellen"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "Benutzername"
@@ -1060,6 +1081,10 @@ msgstr "Warnung"
msgid "Website"
msgstr "Webseite"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr ""

View File

@@ -60,6 +60,11 @@ msgstr "Add user"
msgid "Admin"
msgstr "Admin"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr "Admin user name"
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,14 +142,14 @@ msgstr "Back to log in"
msgid "Blue"
msgstr "Blue"
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr "Browser extension"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr "Browser extension required for Chrome"
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
msgstr "Browser extention"
#: src/components/settings/DisplaySettings.tsx
msgid "Browser tab"
msgstr "Browser tab"
@@ -225,6 +230,10 @@ msgstr "Confirm password"
msgid "Cozy"
msgstr "Cozy"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr "Create Admin Account"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr "Ctrl"
@@ -309,6 +318,8 @@ msgstr "Drag link to bookmark bar"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "In expanded view, scrolling through entries mark them as read"
msgid "Indigo"
msgstr "Indigo"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr "Initial Setup"
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "Parent Category"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr "Unstar"
msgid "Unsubscribe"
msgstr "Unsubscribe"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr "User created."
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "User name"
@@ -1060,6 +1081,10 @@ msgstr "Warning"
msgid "Website"
msgstr "Website"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr "Yellow"

View File

@@ -61,6 +61,11 @@ msgstr "Añadir usuario"
msgid "Admin"
msgstr "Administrador"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -138,14 +143,14 @@ msgstr "Volver a iniciar sesión"
msgid "Blue"
msgstr ""
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr "Extensión del navegador"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr "Se requiere extensión de navegador para Chrome"
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
msgstr "Extensión del navegador"
#: src/components/settings/DisplaySettings.tsx
msgid "Browser tab"
msgstr "Pestaña del navegador"
@@ -226,6 +231,10 @@ msgstr "Confirmar contraseña"
msgid "Cozy"
msgstr "Acogedor"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr "Ctrl"
@@ -310,6 +319,8 @@ msgstr "Arrastra el enlace a la barra de marcadores"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -477,6 +488,10 @@ msgstr "En la vista ampliada, al desplazarse por las entradas marcarlas como le
msgid "Indigo"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -748,6 +763,8 @@ msgid "Parent Category"
msgstr "Categoría principal"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1040,6 +1057,10 @@ msgstr "Desmarcar"
msgid "Unsubscribe"
msgstr "Cancelar suscripción"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "Nombre de usuario"
@@ -1061,6 +1082,10 @@ msgstr "Advertencia"
msgid "Website"
msgstr "Sitio web"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr ""

View File

@@ -60,6 +60,11 @@ msgstr "افزودن کاربر"
msgid "Admin"
msgstr "مدیر"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,12 +142,12 @@ msgstr "بازگشت برای ورود به سیستم"
msgid "Blue"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr ""
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
@@ -225,6 +230,10 @@ msgstr "رمز عبور را تأیید کنید"
msgid "Cozy"
msgstr "دنج"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr ""
@@ -309,6 +318,8 @@ msgstr "پیوند را به نوار نشانک بکشید"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "در نمای بازشده، پیمایش در ورودی‌ها، آن
msgid "Indigo"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "دسته والد"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr ""
msgid "Unsubscribe"
msgstr "لغو اشتراک"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "نام کاربری"
@@ -1060,6 +1081,10 @@ msgstr "هشدار"
msgid "Website"
msgstr "وب سایت"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr ""

View File

@@ -60,6 +60,11 @@ msgstr "Lisää käyttäjä"
msgid "Admin"
msgstr "Järjestelmänvalvoja"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,12 +142,12 @@ msgstr "Takaisin sisäänkirjautumiseen"
msgid "Blue"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr ""
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
@@ -225,6 +230,10 @@ msgstr "Vahvista salasana"
msgid "Cozy"
msgstr "Viihtyisä"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr ""
@@ -309,6 +318,8 @@ msgstr "Vedä linkki kirjanmerkkipalkkiin"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "Merkitse ne luetuiksi laajennetussa näkymässä vierittämällä merkin
msgid "Indigo"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "Pääluokka"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr "Poista tähti"
msgid "Unsubscribe"
msgstr "Peruuta tilaus"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "Käyttäjänimi"
@@ -1060,6 +1081,10 @@ msgstr "Varoitus"
msgid "Website"
msgstr "Verkkosivusto"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr ""

View File

@@ -60,6 +60,11 @@ msgstr "Ajouter un utilisateur"
msgid "Admin"
msgstr "Administrateur"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,14 +142,14 @@ msgstr "Retour à la connexion"
msgid "Blue"
msgstr "Bleu"
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr "Extension navigateur"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr "L'extension navigateur est nécessaire sur Chrome"
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
msgstr "Extension navigateur"
#: src/components/settings/DisplaySettings.tsx
msgid "Browser tab"
msgstr "Onglet navigateur"
@@ -225,6 +230,10 @@ msgstr "Confirmer le mot de passe"
msgid "Cozy"
msgstr "Cozy"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr "Ctrl"
@@ -309,6 +318,8 @@ msgstr "Déplacez le lien vers la barre de favoris"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "En mode de lecture étendu, marquer les éléments comme lus lorsque la
msgid "Indigo"
msgstr "Indigo"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "Catégorie parente"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr "Retirer des favoris"
msgid "Unsubscribe"
msgstr "Se désabonner"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "Nom"
@@ -1060,6 +1081,10 @@ msgstr "Attention"
msgid "Website"
msgstr "Site web"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr "Jaune"

View File

@@ -61,6 +61,11 @@ msgstr "Engadir persoa usuaria"
msgid "Admin"
msgstr "Administración"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -138,14 +143,14 @@ msgstr "Volver para iniciar sesión"
msgid "Blue"
msgstr "Azul"
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr "Complemento do navegador"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr "Complemento para o navegador requerido para Chrome"
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
msgstr "Complemento do navegador"
#: src/components/settings/DisplaySettings.tsx
msgid "Browser tab"
msgstr "Pestana do navegador"
@@ -226,6 +231,10 @@ msgstr "Confirmar contrasinal"
msgid "Cozy"
msgstr "Acolledor"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr "Ctrl"
@@ -310,6 +319,8 @@ msgstr "Arrastra a ligazón á barra de marcadores"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -477,6 +488,10 @@ msgstr "Na vista ampliada, ao desprazarse polas entradas márcaas como lidas"
msgid "Indigo"
msgstr "Índigo"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -748,6 +763,8 @@ msgid "Parent Category"
msgstr "Categoría superior"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1040,6 +1057,10 @@ msgstr "Retirar estrela"
msgid "Unsubscribe"
msgstr "Cancelar a subscrición"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "Identificador"
@@ -1061,6 +1082,10 @@ msgstr "Aviso"
msgid "Website"
msgstr "Páxina web"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr "Amarelo"

View File

@@ -60,6 +60,11 @@ msgstr "Felhasználó hozzáadása"
msgid "Admin"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,12 +142,12 @@ msgstr "Vissza a bejelentkezéshez"
msgid "Blue"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr ""
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
@@ -225,6 +230,10 @@ msgstr "Erősítse meg a jelszót"
msgid "Cozy"
msgstr "Hangulatos"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr ""
@@ -309,6 +318,8 @@ msgstr "Húzza a hivatkozást a könyvjelzősávra"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "Kibontott nézetben a bejegyzések görgetése olvasottként jelöli meg
msgid "Indigo"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "Szülő kategória"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr ""
msgid "Unsubscribe"
msgstr "Leiratkozás"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "Felhasználónév"
@@ -1060,6 +1081,10 @@ msgstr "Figyelem"
msgid "Website"
msgstr "Webhely"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr ""

View File

@@ -60,6 +60,11 @@ msgstr "Tambahkan pengguna"
msgid "Admin"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,12 +142,12 @@ msgstr "Kembali untuk masuk"
msgid "Blue"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr ""
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
@@ -225,6 +230,10 @@ msgstr "Konfirmasi kata sandi"
msgid "Cozy"
msgstr "Nyaman"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr ""
@@ -309,6 +318,8 @@ msgstr "Seret tautan ke bilah bookmark"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "Dalam tampilan yang diperluas, menggulir entri menandainya sebagai telah
msgid "Indigo"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "Kategori Induk"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr "Hapus bintang"
msgid "Unsubscribe"
msgstr "Berhenti berlangganan"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "Nama pengguna"
@@ -1060,6 +1081,10 @@ msgstr "Peringatan"
msgid "Website"
msgstr "Situs Web"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr ""

View File

@@ -60,6 +60,11 @@ msgstr "Aggiungi utente"
msgid "Admin"
msgstr "Ammin"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,12 +142,12 @@ msgstr "Torna per accedere"
msgid "Blue"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr ""
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
@@ -225,6 +230,10 @@ msgstr "Conferma password"
msgid "Cozy"
msgstr "Accogliente"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr "ctrl"
@@ -309,6 +318,8 @@ msgstr "Trascina il collegamento sulla barra dei preferiti"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "Nella vista espansa, scorrendo le voci contrassegnale come lette"
msgid "Indigo"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "Categoria padre"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr "Elimina le stelle"
msgid "Unsubscribe"
msgstr "Annulla iscrizione"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "Nome utente"
@@ -1060,6 +1081,10 @@ msgstr "Avviso"
msgid "Website"
msgstr "Sito web"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr ""

View File

@@ -60,6 +60,11 @@ msgstr "ユーザー追加"
msgid "Admin"
msgstr "管理者"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,14 +142,14 @@ msgstr "ログインに戻る"
msgid "Blue"
msgstr ""
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr "ブラウザー拡張"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr "Chromeのブラウザー拡張が必要です"
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
msgstr "ブラウザー拡張"
#: src/components/settings/DisplaySettings.tsx
msgid "Browser tab"
msgstr "ブラウザータブ"
@@ -225,6 +230,10 @@ msgstr "パスワード確認"
msgid "Cozy"
msgstr "Cozy"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr "Ctrl"
@@ -309,6 +318,8 @@ msgstr "リンクをブックマークバーにドラッグ"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "展開ビューでエントリーをスクロールすると、それら
msgid "Indigo"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "親カテゴリ"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr "スターを外す"
msgid "Unsubscribe"
msgstr "退会"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "ユーザー名"
@@ -1060,6 +1081,10 @@ msgstr "警告"
msgid "Website"
msgstr "ウェブサイト"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr ""

View File

@@ -60,6 +60,11 @@ msgstr "사용자 추가"
msgid "Admin"
msgstr "관리자"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,12 +142,12 @@ msgstr "로그인으로 돌아가기"
msgid "Blue"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr ""
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
@@ -225,6 +230,10 @@ msgstr "비밀번호 확인"
msgid "Cozy"
msgstr "코지"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr "컨트롤"
@@ -309,6 +318,8 @@ msgstr "링크를 북마크바로 드래그"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "확장 보기에서 항목을 스크롤하면 읽은 것으로 표시됩
msgid "Indigo"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "부모 카테고리"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr "별표 제거"
msgid "Unsubscribe"
msgstr "구독 취소"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "사용자 이름"
@@ -1060,6 +1081,10 @@ msgstr "경고"
msgid "Website"
msgstr "웹사이트"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr ""

View File

@@ -60,6 +60,11 @@ msgstr "Tambah pengguna"
msgid "Admin"
msgstr "Pentadbir"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,12 +142,12 @@ msgstr "Kembali untuk log masuk"
msgid "Blue"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr ""
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
@@ -225,6 +230,10 @@ msgstr "Sahkan kata laluan"
msgid "Cozy"
msgstr "Nyaman"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr ""
@@ -309,6 +318,8 @@ msgstr "Seret pautan ke bar penanda halaman"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "Dalam paparan yang diperluas, menatal melalui entri menandakannya sebaga
msgid "Indigo"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "Kategori Induk"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr "Nyahbintang"
msgid "Unsubscribe"
msgstr "Nyahlanggan"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "Nama pengguna"
@@ -1060,6 +1081,10 @@ msgstr "Amaran"
msgid "Website"
msgstr "Laman web"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr ""

View File

@@ -60,6 +60,11 @@ msgstr "Legg til bruker"
msgid "Admin"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,12 +142,12 @@ msgstr "Tilbake for å logge inn"
msgid "Blue"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr ""
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
@@ -225,6 +230,10 @@ msgstr "Bekreft passord"
msgid "Cozy"
msgstr "Koselig"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr ""
@@ -309,6 +318,8 @@ msgstr "Dra lenken til bokmerkelinjen"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "I utvidet visning merker du dem som lest ved å rulle gjennom oppføring
msgid "Indigo"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "Overordnet kategori"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr "Fjern stjerne"
msgid "Unsubscribe"
msgstr "Avslutt abonnementet"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "Brukernavn"
@@ -1060,6 +1081,10 @@ msgstr "Advarsel"
msgid "Website"
msgstr "Nettsted"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr ""

View File

@@ -60,6 +60,11 @@ msgstr "Gebruiker toevoegen"
msgid "Admin"
msgstr "Beheerder"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,12 +142,12 @@ msgstr "Terug naar inloggen"
msgid "Blue"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr ""
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
@@ -225,6 +230,10 @@ msgstr "Bevestig wachtwoord"
msgid "Cozy"
msgstr "Gezellig"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr ""
@@ -309,6 +318,8 @@ msgstr "Link naar bladwijzerbalk slepen"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "In de uitgevouwen weergave markeert het scrollen door items ze als gelez
msgid "Indigo"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "Oudercategorie"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr "Sterren uit"
msgid "Unsubscribe"
msgstr "Afmelden"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "Gebruikersnaam"
@@ -1060,6 +1081,10 @@ msgstr "Waarschuwing"
msgid "Website"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr ""

View File

@@ -60,6 +60,11 @@ msgstr "Legg til bruker"
msgid "Admin"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,12 +142,12 @@ msgstr "Tilbake for å logge inn"
msgid "Blue"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr ""
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
@@ -225,6 +230,10 @@ msgstr "Bekreft passord"
msgid "Cozy"
msgstr "Koselig"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr ""
@@ -309,6 +318,8 @@ msgstr "Dra lenken til bokmerkelinjen"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "I utvidet visning merker du dem som lest ved å rulle gjennom oppføring
msgid "Indigo"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "Overordnet kategori"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr "Fjern stjerne"
msgid "Unsubscribe"
msgstr "Avslutt abonnementet"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "Brukernavn"
@@ -1060,6 +1081,10 @@ msgstr "Advarsel"
msgid "Website"
msgstr "Nettsted"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr ""

View File

@@ -60,6 +60,11 @@ msgstr "Dodaj użytkownika"
msgid "Admin"
msgstr "Administracja"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,12 +142,12 @@ msgstr "Powrót do logowania"
msgid "Blue"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr ""
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
@@ -225,6 +230,10 @@ msgstr "Potwierdź hasło"
msgid "Cozy"
msgstr "Przytulny"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr ""
@@ -309,6 +318,8 @@ msgstr "Przeciągnij link do paska zakładek"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "W widoku rozszerzonym przewijanie wpisów oznacza je jako przeczytane"
msgid "Indigo"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "Kategoria nadrzędna"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr ""
msgid "Unsubscribe"
msgstr "Anuluj subskrypcję"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "Nazwa użytkownika"
@@ -1060,6 +1081,10 @@ msgstr "Ostrzeżenie"
msgid "Website"
msgstr "Strona internetowa"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr ""

View File

@@ -60,6 +60,11 @@ msgstr "Adicionar usuário"
msgid "Admin"
msgstr "Administrador"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,14 +142,14 @@ msgstr "Voltar para logar"
msgid "Blue"
msgstr "Azul"
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr "Extensão do navegador"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr "Extensão para o Chrome necessária"
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
msgstr "Extensão do navegador"
#: src/components/settings/DisplaySettings.tsx
msgid "Browser tab"
msgstr "Aba do navegador"
@@ -225,6 +230,10 @@ msgstr "Confirmar senha"
msgid "Cozy"
msgstr "Aconchegante"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr "Ctrl"
@@ -309,6 +318,8 @@ msgstr "Arraste o link para a barra de favoritos"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "Na visualização expandida, rolar pelas entradas marca-as como lidas"
msgid "Indigo"
msgstr "Índigo"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "Categoria Pai"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr "Desestrelar"
msgid "Unsubscribe"
msgstr "Cancelar inscrição"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "Nome de usuário"
@@ -1060,6 +1081,10 @@ msgstr "Aviso"
msgid "Website"
msgstr "Site"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr "Amarelo"

View File

@@ -60,6 +60,11 @@ msgstr "Добавить пользователя"
msgid "Admin"
msgstr "Админ"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,14 +142,14 @@ msgstr "Вернуться к входу"
msgid "Blue"
msgstr ""
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr "Расширение для браузера"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr "Для браузера Chrome требуется расширение"
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
msgstr "Расширение для браузера"
#: src/components/settings/DisplaySettings.tsx
msgid "Browser tab"
msgstr ""
@@ -225,6 +230,10 @@ msgstr "Подтвердить пароль"
msgid "Cozy"
msgstr "Уютно"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr "Ctrl"
@@ -309,6 +318,8 @@ msgstr "Перетащите ссылку на панель закладок"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "В развернутом виде прокрутка записей п
msgid "Indigo"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "Родительская категория"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr "Удалить из избранного"
msgid "Unsubscribe"
msgstr "Отписаться"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "Имя пользователя"
@@ -1060,6 +1081,10 @@ msgstr "Предупреждение"
msgid "Website"
msgstr "Веб-сайт"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr ""

View File

@@ -60,6 +60,11 @@ msgstr "Pridať užívateľa"
msgid "Admin"
msgstr "Správca"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,12 +142,12 @@ msgstr "Späť na prihlásenie"
msgid "Blue"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr ""
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
@@ -225,6 +230,10 @@ msgstr "Potvrďte heslo"
msgid "Cozy"
msgstr "Útulný"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr ""
@@ -309,6 +318,8 @@ msgstr "Presuňte odkaz na lištu so záložkami"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "V rozšírenom zobrazení ich rolovanie cez položky označí ako preč
msgid "Indigo"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "Rodičovská kategória"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr "Odobrať hviezdičku"
msgid "Unsubscribe"
msgstr "Zrušte odber"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "Meno používateľa"
@@ -1060,6 +1081,10 @@ msgstr "Varovanie"
msgid "Website"
msgstr "Webová stránka"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr ""

View File

@@ -60,6 +60,11 @@ msgstr "Lägg till användare"
msgid "Admin"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,12 +142,12 @@ msgstr "Tillbaka för att logga in"
msgid "Blue"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr ""
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr ""
#: src/components/settings/DisplaySettings.tsx
@@ -225,6 +230,10 @@ msgstr "Bekräfta lösenord"
msgid "Cozy"
msgstr "Mysigt"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr ""
@@ -309,6 +318,8 @@ msgstr "Dra länken till bokmärkesfältet"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "I utökad vy, rullning genom poster markerar dem som lästa"
msgid "Indigo"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "Föräldrakategori"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr ""
msgid "Unsubscribe"
msgstr "Avregistrera"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "Användarnamn"
@@ -1060,6 +1081,10 @@ msgstr "Varning"
msgid "Website"
msgstr "Webbplats"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr ""

View File

@@ -60,6 +60,11 @@ msgstr "Kullanıcı ekle"
msgid "Admin"
msgstr "Yönetici"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,14 +142,14 @@ msgstr "Giriş yapmak için geri dön"
msgid "Blue"
msgstr ""
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr "Tarayıcı eklentisi"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr ""
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
msgstr "Tarayıcı eklentisi"
#: src/components/settings/DisplaySettings.tsx
msgid "Browser tab"
msgstr ""
@@ -225,6 +230,10 @@ msgstr "Şifreyi onayla"
msgid "Cozy"
msgstr "Rahat"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr "Ctrl"
@@ -309,6 +318,8 @@ msgstr "Bağlantıyı yer işareti çubuğuna sürükleyin"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "Genişletilmiş görünümde, girişler arasında gezinmek onları okund
msgid "Indigo"
msgstr ""
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "Üst Kategori"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr "Yıldızı kaldır"
msgid "Unsubscribe"
msgstr "Aboneliği iptal et"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "Kullanıcı adı"
@@ -1060,6 +1081,10 @@ msgstr "Uyarı"
msgid "Website"
msgstr "Web sitesi"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr ""

View File

@@ -60,6 +60,11 @@ msgstr "添加用户"
msgid "Admin"
msgstr "管理员"
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
msgid "Admin user name"
msgstr ""
#: src/components/content/add/CategorySelect.tsx
#: src/components/header/Header.tsx
#: src/components/sidebar/Tree.tsx
@@ -137,12 +142,12 @@ msgstr "返回登录"
msgid "Blue"
msgstr "蓝"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
#: src/pages/app/AboutPage.tsx
msgid "Browser extension"
msgstr "浏览器扩展"
#: src/pages/app/AboutPage.tsx
msgid "Browser extention"
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Browser extension required for Chrome"
msgstr "浏览器扩展"
#: src/components/settings/DisplaySettings.tsx
@@ -225,6 +230,10 @@ msgstr "确认密码"
msgid "Cozy"
msgstr "宽松"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Create Admin Account"
msgstr ""
#: src/components/KeyboardShortcutsHelp.tsx
msgid "Ctrl"
msgstr "Ctrl"
@@ -309,6 +318,8 @@ msgstr "拖动链接到书签栏"
#: src/components/admin/UserEdit.tsx
#: src/components/settings/ProfileSettings.tsx
#: src/pages/admin/AdminUsersPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
#: src/pages/auth/PasswordRecoveryPage.tsx
msgid "E-mail"
@@ -476,6 +487,10 @@ msgstr "在展开视图中,滚动条目将它们标记为已读"
msgid "Indigo"
msgstr "靛蓝"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Initial Setup"
msgstr ""
#: src/components/content/FeedEntryContextMenu.tsx
#: src/components/content/FeedEntryFooter.tsx
msgid "Keep unread"
@@ -747,6 +762,8 @@ msgid "Parent Category"
msgstr "父类别"
#: src/components/admin/UserEdit.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/InitialSetupPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
@@ -1039,6 +1056,10 @@ msgstr "取消星标"
msgid "Unsubscribe"
msgstr "取消订阅"
#: src/pages/auth/InitialSetupPage.tsx
msgid "User created."
msgstr ""
#: src/components/settings/ProfileSettings.tsx
msgid "User name"
msgstr "用户名"
@@ -1060,6 +1081,10 @@ msgstr "警告"
msgid "Website"
msgstr "网站"
#: src/pages/auth/InitialSetupPage.tsx
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr ""
#: src/components/settings/DisplaySettings.tsx
msgid "Yellow"
msgstr "黄"

View File

@@ -107,7 +107,7 @@ export function AboutPage() {
<List>
<List.Item>
<Anchor href={Constants.browserExtensionUrl} target="_blank" rel="noreferrer">
<Trans>Browser extention</Trans>
<Trans>Browser extension</Trans>
</Anchor>
</List.Item>
<List.Item>

View File

@@ -0,0 +1,89 @@
import { msg } from "@lingui/core/macro"
import { useLingui } from "@lingui/react"
import { Trans } from "@lingui/react/macro"
import { Box, Button, Container, Paper, PasswordInput, Stack, TextInput, Title } from "@mantine/core"
import { useForm } from "@mantine/form"
import { useAsyncCallback } from "react-async-hook"
import { client, errorToStrings } from "@/app/client"
import { redirectToRootCategory } from "@/app/redirect/thunks"
import { useAppDispatch } from "@/app/store"
import type { InitialSetupRequest } from "@/app/types"
import { Alert } from "@/components/Alert"
import { PageTitle } from "@/pages/PageTitle"
export function InitialSetupPage() {
const dispatch = useAppDispatch()
const { _ } = useLingui()
const form = useForm<InitialSetupRequest>({
initialValues: {
name: "",
password: "",
email: "",
},
})
const login = useAsyncCallback(client.user.login, {
onSuccess: () => {
dispatch(redirectToRootCategory())
},
})
const setup = useAsyncCallback(client.user.initialSetup, {
onSuccess: () => {
login.execute(form.values)
},
})
return (
<Container size="xs">
<PageTitle />
<Paper>
<Title order={2} mb="md">
<Trans>Initial Setup</Trans>
</Title>
<Box mb="md">
<Trans>
Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get
started.
</Trans>
</Box>
{setup.error && (
<Box mb="md">
<Alert messages={errorToStrings(setup.error)} />
</Box>
)}
<form onSubmit={form.onSubmit(setup.execute)}>
<Stack>
<TextInput
label={<Trans>Admin user name</Trans>}
placeholder={_(msg`Admin user name`)}
{...form.getInputProps("name")}
size="md"
required
autoCapitalize="off"
/>
<PasswordInput
label={<Trans>Password</Trans>}
placeholder={_(msg`Password`)}
{...form.getInputProps("password")}
size="md"
required
/>
<TextInput
type="email"
label={<Trans>E-mail</Trans>}
placeholder={_(msg`E-mail`)}
{...form.getInputProps("email")}
size="md"
/>
<Button type="submit" loading={setup.loading}>
<Trans>Create Admin Account</Trans>
</Button>
</Stack>
</form>
</Paper>
</Container>
)
}

View File

@@ -72,7 +72,7 @@ export function RegistrationPage() {
placeholder={_(msg`E-mail address`)}
{...form.getInputProps("email")}
size="md"
required
required={serverInfos.emailAddressRequired}
/>
<PasswordInput
label={<Trans>Password</Trans>}

View File

@@ -6,13 +6,13 @@
<parent>
<groupId>com.commafeed</groupId>
<artifactId>commafeed</artifactId>
<version>5.12.1</version>
<version>6.0.0</version>
</parent>
<artifactId>commafeed-server</artifactId>
<name>CommaFeed Server</name>
<properties>
<quarkus.version>3.30.5</quarkus.version>
<quarkus.version>3.30.6</quarkus.version>
<querydsl.version>7.1</querydsl.version>
<rome.version>2.1.0</rome.version>
@@ -301,7 +301,7 @@
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>12.3.0</version>
<version>13.0.0</version>
</dependency>
</dependencies>
<executions>
@@ -359,7 +359,7 @@
<dependency>
<groupId>com.commafeed</groupId>
<artifactId>commafeed-client</artifactId>
<version>5.12.1</version>
<version>6.0.0</version>
</dependency>
<!-- compile-time processors -->
@@ -494,12 +494,12 @@
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.21.2</version>
<version>1.22.1</version>
</dependency>
<dependency>
<groupId>com.ibm.icu</groupId>
<artifactId>icu4j</artifactId>
<version>78.1</version>
<version>78.2</version>
</dependency>
<dependency>
<groupId>net.sourceforge.cssparser</groupId>
@@ -519,7 +519,7 @@
<dependency>
<groupId>io.github.hakky54</groupId>
<artifactId>ayza-for-apache5</artifactId>
<version>10.0.2</version>
<version>10.0.3</version>
</dependency>
<dependency>
<groupId>org.brotli</groupId>

View File

@@ -4,11 +4,11 @@ Official docker images for https://github.com/Athou/commafeed/
## Quickstart
Start CommaFeed with a H2 embedded database. Then login as `admin/admin` on http://localhost:8082/
Start CommaFeed with a H2 embedded database. The app will be accessible on http://localhost:8082/
### docker
`docker run --name commafeed --detach --publish 8082:8082 --restart unless-stopped --volume /path/to/commafeed/db:/commafeed/data --memory 256M athou/commafeed:latest-h2`
`docker run --name commafeed --detach --publish 8082:8082 --restart unless-stopped --volume /path/to/commafeed/data:/commafeed/data --memory 256M athou/commafeed:latest-h2`
### docker-compose
@@ -18,7 +18,7 @@ services:
image: athou/commafeed:latest-h2
restart: unless-stopped
volumes:
- /path/to/commafeed/db:/commafeed/data
- ./data:/commafeed/data
deploy:
resources:
limits:
@@ -56,7 +56,7 @@ services:
POSTGRES_PASSWORD: commafeed
POSTGRES_DB: commafeed
volumes:
- /path/to/commafeed/db:/var/lib/postgresql/data
- ./data:/var/lib/postgresql
```
CommaFeed also supports:

View File

@@ -4,7 +4,6 @@ import jakarta.enterprise.event.Observes;
import jakarta.inject.Singleton;
import com.commafeed.backend.feed.FeedRefreshEngine;
import com.commafeed.backend.service.db.DatabaseStartupService;
import com.commafeed.backend.task.TaskScheduler;
import com.commafeed.security.password.PasswordConstraintValidator;
@@ -16,15 +15,12 @@ import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class CommaFeedApplication {
private final DatabaseStartupService databaseStartupService;
private final FeedRefreshEngine feedRefreshEngine;
private final TaskScheduler taskScheduler;
private final CommaFeedConfiguration config;
public void start(@Observes StartupEvent ev) {
PasswordConstraintValidator.setStrict(config.users().strictPasswordPolicy());
databaseStartupService.populateInitialData();
PasswordConstraintValidator.setMinimumPasswordLength(config.users().minimumPasswordLength());
feedRefreshEngine.start();
taskScheduler.start();

View File

@@ -326,10 +326,16 @@ public interface CommaFeedConfiguration {
boolean allowRegistrations();
/**
* Whether to enable strict password validation (1 uppercase char, 1 lowercase char, 1 digit, 1 special char).
* Minimum password length for user accounts.
*/
@WithDefault("true")
boolean strictPasswordPolicy();
@WithDefault("4")
int minimumPasswordLength();
/**
* Whether an email address is required when creating a user account.
*/
@WithDefault("false")
boolean emailAddressRequired();
/**
* Whether to create a demo account the first time the app starts.

View File

@@ -4,6 +4,5 @@ import lombok.experimental.UtilityClass;
@UtilityClass
public class CommaFeedConstants {
public static final String USERNAME_ADMIN = "admin";
public static final String USERNAME_DEMO = "demo";
}

View File

@@ -2,12 +2,16 @@ package com.commafeed;
import jakarta.annotation.Priority;
import jakarta.validation.ValidationException;
import jakarta.ws.rs.core.NewCookie;
import jakarta.ws.rs.ext.Provider;
import org.jboss.resteasy.reactive.RestResponse;
import org.jboss.resteasy.reactive.RestResponse.ResponseBuilder;
import org.jboss.resteasy.reactive.RestResponse.Status;
import org.jboss.resteasy.reactive.server.ServerExceptionMapper;
import com.commafeed.security.CookieService;
import io.quarkus.runtime.annotations.RegisterForReflection;
import io.quarkus.security.AuthenticationFailedException;
import io.quarkus.security.UnauthorizedException;
@@ -18,17 +22,18 @@ import lombok.RequiredArgsConstructor;
@Priority(1)
public class ExceptionMappers {
private final CookieService cookieService;
private final CommaFeedConfiguration config;
@ServerExceptionMapper(UnauthorizedException.class)
public RestResponse<UnauthorizedResponse> unauthorized(UnauthorizedException e) {
return RestResponse.status(RestResponse.Status.UNAUTHORIZED,
new UnauthorizedResponse(e.getMessage(), config.users().allowRegistrations()));
return RestResponse.status(Status.UNAUTHORIZED, new UnauthorizedResponse(e.getMessage(), config.users().allowRegistrations()));
}
@ServerExceptionMapper(AuthenticationFailedException.class)
public RestResponse<AuthenticationFailed> authenticationFailed(AuthenticationFailedException e) {
return RestResponse.status(RestResponse.Status.UNAUTHORIZED, new AuthenticationFailed(e.getMessage()));
NewCookie logoutCookie = cookieService.buildLogoutCookie();
return ResponseBuilder.create(Status.UNAUTHORIZED, new AuthenticationFailed(e.getMessage())).cookie(logoutCookie).build();
}
@ServerExceptionMapper(ValidationException.class)

View File

@@ -11,8 +11,8 @@ import java.time.InstantSource;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.SequencedMap;
import java.util.concurrent.ExecutionException;
import java.util.stream.Stream;
import java.util.zip.GZIPInputStream;
@@ -294,7 +294,7 @@ public class HttpGetter {
headers.add(new BasicHeader(HttpHeaders.PRAGMA, "No-cache"));
headers.add(new BasicHeader(HttpHeaders.CACHE_CONTROL, "no-cache"));
Map<String, InputStreamFactory> contentDecoderMap = new LinkedHashMap<>();
SequencedMap<String, InputStreamFactory> contentDecoderMap = new LinkedHashMap<>();
contentDecoderMap.put(ContentCoding.GZIP.token(), GZIPInputStream::new);
contentDecoderMap.put(ContentCoding.DEFLATE.token(), DeflateInputStream::new);
contentDecoderMap.put(ContentCoding.BROTLI.token(), BrotliInputStream::new);

View File

@@ -32,4 +32,8 @@ public class UserRoleDAO extends GenericDAO<UserRole> {
public Set<Role> findRoles(User user) {
return findAll(user).stream().map(UserRole::getRole).collect(Collectors.toSet());
}
public long countAdmins() {
return query().select(ROLE.count()).from(ROLE).where(ROLE.role.eq(Role.ADMIN)).fetchOne();
}
}

View File

@@ -101,7 +101,7 @@ public class DefaultFaviconFetcher extends AbstractFaviconFetcher {
return null;
}
String href = icons.get(0).attr("abs:href");
String href = icons.getFirst().attr("abs:href");
if (StringUtils.isBlank(href)) {
log.debug("No icon found in page");
return null;

View File

@@ -17,7 +17,7 @@ public record Favicon(byte[] icon, MediaType mediaType) {
try {
return MediaType.valueOf(contentType);
} catch (Exception e) {
log.debug("invalid content type '{}' received, returning default value", contentType);
log.debug("invalid content type '{}' received, returning default value", contentType, e);
return DEFAULT_MEDIA_TYPE;
}
}

View File

@@ -164,9 +164,11 @@ public class FeedRefreshEngine {
Instant lastLoginThreshold = config.feedRefresh().userInactivityPeriod().isZero() ? null
: Instant.now().minus(config.feedRefresh().userInactivityPeriod());
List<Feed> feeds = feedDAO.findNextUpdatable(max, lastLoginThreshold);
// update disabledUntil to prevent feeds from being returned again by feedDAO.findNextUpdatable()
Instant nextUpdateDate = Instant.now().plus(config.feedRefresh().interval());
feedDAO.setDisabledUntil(feeds.stream().map(AbstractModel::getId).toList(), nextUpdateDate);
if (!feeds.isEmpty()) {
// update disabledUntil to prevent feeds from being returned again by feedDAO.findNextUpdatable()
Instant nextUpdateDate = Instant.now().plus(config.feedRefresh().interval());
feedDAO.setDisabledUntil(feeds.stream().map(AbstractModel::getId).toList(), nextUpdateDate);
}
return feeds;
});
}

View File

@@ -30,7 +30,11 @@ public class OPMLImporter {
private final FeedSubscriptionService feedSubscriptionService;
public void importOpml(User user, String xml) throws IllegalArgumentException, FeedException {
xml = xml.substring(xml.indexOf('<'));
int index = xml.indexOf('<');
if (index == -1) {
throw new IllegalArgumentException("Invalid OPML: no start tag found");
}
xml = xml.substring(index);
WireFeedInput input = new WireFeedInput();
Opml feed = (Opml) input.build(new StringReader(xml));
List<Outline> outlines = feed.getOutlines();

View File

@@ -1,7 +1,6 @@
package com.commafeed.backend.service;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;
@@ -139,10 +138,6 @@ public class UserService {
return user;
}
public void createAdminUser() {
register(CommaFeedConstants.USERNAME_ADMIN, "admin", "admin@commafeed.com", Arrays.asList(Role.ADMIN, Role.USER), true);
}
public void createDemoUser() {
register(CommaFeedConstants.USERNAME_DEMO, "demo", "demo@commafeed.com", Collections.singletonList(Role.USER), true);
}

View File

@@ -25,23 +25,8 @@ public class DatabaseStartupService {
private final UserService userService;
private final CommaFeedConfiguration config;
public void populateInitialData() {
long count = unitOfWork.call(userDAO::count);
if (count == 0) {
unitOfWork.run(this::initialData);
}
}
private void initialData() {
log.info("populating database with default values");
try {
userService.createAdminUser();
if (config.users().createDemoAccount()) {
userService.createDemoUser();
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
public boolean isInitialSetupRequired() {
return unitOfWork.call(userDAO::count) == 0;
}
/**

View File

@@ -14,7 +14,7 @@ public class InPageReferenceFeedURLProvider implements FeedURLProvider {
@Override
public List<String> get(String url, String urlContent) {
Document doc = Jsoup.parse(urlContent, url);
if (!"html".equals(doc.children().get(0).tagName())) {
if (!"html".equals(doc.children().getFirst().tagName())) {
return List.of();
}
return Stream.concat(doc.select("link[type=application/atom+xml]").stream(), doc.select("link[type=application/rss+xml]").stream())

View File

@@ -25,6 +25,9 @@ public class ServerInfo implements Serializable {
@Schema(required = true)
private boolean allowRegistrations;
@Schema(required = true)
private boolean emailAddressRequired;
@Schema(required = true)
private boolean smtpEnabled;
@@ -43,4 +46,7 @@ public class ServerInfo implements Serializable {
@Schema(required = true)
private long forceRefreshCooldownDuration;
@Schema(required = true)
private boolean initialSetupRequired;
}

View File

@@ -0,0 +1,25 @@
package com.commafeed.frontend.model.request;
import java.io.Serializable;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import com.commafeed.security.password.ValidPassword;
import lombok.Data;
@SuppressWarnings("serial")
@Schema(description = "Initial admin account setup request")
@Data
public class InitialSetupRequest implements Serializable {
@Schema(description = "admin username", required = true)
private String name;
@Schema(description = "admin password", required = true)
@ValidPassword
private String password;
@Schema(description = "admin email")
private String email;
}

View File

@@ -26,7 +26,6 @@ import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import com.codahale.metrics.MetricRegistry;
import com.commafeed.CommaFeedConstants;
import com.commafeed.backend.dao.UserDAO;
import com.commafeed.backend.dao.UserRoleDAO;
import com.commafeed.backend.model.User;
@@ -101,8 +100,8 @@ public class AdminREST {
if (req.isAdmin() && !roles.contains(Role.ADMIN)) {
userRoleDAO.persist(new UserRole(u, Role.ADMIN));
} else if (!req.isAdmin() && roles.contains(Role.ADMIN)) {
if (CommaFeedConstants.USERNAME_ADMIN.equals(u.getName())) {
return Response.status(Status.FORBIDDEN).entity("You cannot remove the admin role from the admin user.").build();
if (userRoleDAO.countAdmins() == 1) {
return Response.status(Status.FORBIDDEN).entity("You cannot remove the admin role from the last admin user.").build();
}
for (UserRole userRole : userRoleDAO.findAll(u)) {
if (userRole.getRole() == Role.ADMIN) {

View File

@@ -178,7 +178,7 @@ public class CategoryREST {
boolean hasMore = entries.getEntries().size() > limit;
if (hasMore) {
entries.setHasMore(true);
entries.getEntries().remove(entries.getEntries().size() - 1);
entries.getEntries().removeLast();
}
entries.setTimestamp(System.currentTimeMillis());
@@ -337,7 +337,7 @@ public class CategoryREST {
}
FeedCategory parent = null;
if (req.getParentId() != null && !CategoryREST.ALL.equals(req.getParentId())
if (req.getParentId() != null && !ALL.equals(req.getParentId())
&& !Strings.CS.equals(req.getParentId(), String.valueOf(req.getId()))) {
parent = feedCategoryDAO.findById(user, Long.valueOf(req.getParentId()));
}

View File

@@ -181,7 +181,7 @@ public class FeedREST {
boolean hasMore = entries.getEntries().size() > limit;
if (hasMore) {
entries.setHasMore(true);
entries.getEntries().remove(entries.getEntries().size() - 1);
entries.getEntries().removeLast();
}
} else {
return Response.status(Status.NOT_FOUND).entity("<message>feed not found</message>").build();

View File

@@ -22,6 +22,7 @@ import com.commafeed.CommaFeedVersion;
import com.commafeed.backend.HttpGetter;
import com.commafeed.backend.HttpGetter.HttpResult;
import com.commafeed.backend.feed.ImageProxyUrl;
import com.commafeed.backend.service.db.DatabaseStartupService;
import com.commafeed.frontend.model.ServerInfo;
import com.commafeed.security.Roles;
@@ -39,6 +40,7 @@ public class ServerREST {
private final HttpGetter httpGetter;
private final CommaFeedConfiguration config;
private final CommaFeedVersion version;
private final DatabaseStartupService databaseStartupService;
@Path("/get")
@GET
@@ -51,12 +53,14 @@ public class ServerREST {
infos.setVersion(version.getVersion());
infos.setGitCommit(version.getGitCommit());
infos.setAllowRegistrations(config.users().allowRegistrations());
infos.setEmailAddressRequired(config.users().emailAddressRequired());
infos.setSmtpEnabled(config.passwordRecoveryEnabled());
infos.setDemoAccountEnabled(config.users().createDemoAccount());
infos.setWebsocketEnabled(config.websocket().enabled());
infos.setWebsocketPingInterval(config.websocket().pingInterval().toMillis());
infos.setTreeReloadInterval(config.websocket().treeReloadInterval().toMillis());
infos.setForceRefreshCooldownDuration(config.feedRefresh().forceRefreshCooldownDuration().toMillis());
infos.setInitialSetupRequired(databaseStartupService.isInitialSetupRequired());
return infos;
}

View File

@@ -5,7 +5,9 @@ import java.net.URISyntaxException;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import jakarta.annotation.security.PermitAll;
@@ -36,6 +38,7 @@ import com.commafeed.CommaFeedConfiguration;
import com.commafeed.CommaFeedConstants;
import com.commafeed.backend.Digests;
import com.commafeed.backend.Urls;
import com.commafeed.backend.dao.UnitOfWork;
import com.commafeed.backend.dao.UserDAO;
import com.commafeed.backend.dao.UserRoleDAO;
import com.commafeed.backend.dao.UserSettingsDAO;
@@ -50,8 +53,10 @@ import com.commafeed.backend.model.UserSettings.ScrollMode;
import com.commafeed.backend.service.MailService;
import com.commafeed.backend.service.PasswordEncryptionService;
import com.commafeed.backend.service.UserService;
import com.commafeed.backend.service.db.DatabaseStartupService;
import com.commafeed.frontend.model.Settings;
import com.commafeed.frontend.model.UserModel;
import com.commafeed.frontend.model.request.InitialSetupRequest;
import com.commafeed.frontend.model.request.PasswordResetRequest;
import com.commafeed.frontend.model.request.ProfileModificationRequest;
import com.commafeed.frontend.model.request.RegistrationRequest;
@@ -78,9 +83,11 @@ public class UserREST {
private final UserSettingsDAO userSettingsDAO;
private final UserService userService;
private final PasswordEncryptionService encryptionService;
private final DatabaseStartupService databaseStartupService;
private final MailService mailService;
private final CommaFeedConfiguration config;
private final UriInfo uri;
private final UnitOfWork unitOfWork;
@Path("/settings")
@GET
@@ -231,7 +238,7 @@ public class UserREST {
public Response saveUserProfile(@Valid @Parameter(required = true) ProfileModificationRequest request) {
User user = authenticationContext.getCurrentUser();
if (CommaFeedConstants.USERNAME_DEMO.equals(user.getName())) {
return Response.status(Status.FORBIDDEN).build();
return Response.status(Status.FORBIDDEN).entity("the profile of the demo account cannot be modified").build();
}
Optional<User> login = userService.login(user.getName(), request.getCurrentPassword());
@@ -276,6 +283,31 @@ public class UserREST {
}
}
@Path("/initialSetup")
@PermitAll
@POST
@Transactional
@Operation(
summary = "Create the initial admin account",
description = "This endpoint is only available when no users exist in the database")
public Response initialSetup(@Valid @Parameter(required = true) InitialSetupRequest req) {
boolean initialSetupRequired = databaseStartupService.isInitialSetupRequired();
if (!initialSetupRequired) {
return Response.status(Status.BAD_REQUEST).entity("Initial setup has already been completed").build();
}
userService.register(req.getName(), req.getPassword(), req.getEmail(), List.of(Role.ADMIN, Role.USER), true);
if (config.users().createDemoAccount()) {
User demo = userDAO.findByName(CommaFeedConstants.USERNAME_DEMO);
if (demo == null) {
userService.createDemoUser();
}
}
return Response.ok().build();
}
@Path("/passwordReset")
@PermitAll
@POST
@@ -361,9 +393,15 @@ public class UserREST {
@Operation(summary = "Delete the user account")
public Response deleteUser() {
User user = authenticationContext.getCurrentUser();
if (CommaFeedConstants.USERNAME_ADMIN.equals(user.getName()) || CommaFeedConstants.USERNAME_DEMO.equals(user.getName())) {
return Response.status(Status.FORBIDDEN).build();
if (CommaFeedConstants.USERNAME_DEMO.equals(user.getName())) {
return Response.status(Status.FORBIDDEN).entity("the demo account cannot be deleted").build();
}
Set<Role> roles = userRoleDAO.findRoles(user);
if (roles.contains(Role.ADMIN) && userRoleDAO.countAdmins() == 1) {
return Response.status(Status.FORBIDDEN).entity("The last admin account cannot be deleted").build();
}
userService.unregister(userDAO.findById(user.getId()));
return Response.ok().build();
}

View File

@@ -94,8 +94,8 @@ public class FeverREST {
@Operation(hidden = true)
public FeverResponse formUrlencoded(@Context UriInfo uri, @PathParam("userId") Long userId, MultivaluedMap<String, String> form) {
Map<String, String> params = new HashMap<>();
uri.getQueryParameters().forEach((k, v) -> params.put(k, v.get(0)));
form.forEach((k, v) -> params.put(k, v.get(0)));
uri.getQueryParameters().forEach((k, v) -> params.put(k, v.getFirst()));
form.forEach((k, v) -> params.put(k, v.getFirst()));
return handle(userId, params);
}
@@ -107,7 +107,7 @@ public class FeverREST {
@Operation(hidden = true)
public FeverResponse noForm(@Context UriInfo uri, @PathParam("userId") Long userId) {
Map<String, String> params = new HashMap<>();
uri.getQueryParameters().forEach((k, v) -> params.put(k, v.get(0)));
uri.getQueryParameters().forEach((k, v) -> params.put(k, v.getFirst()));
return handle(userId, params);
}
@@ -119,7 +119,7 @@ public class FeverREST {
@Operation(hidden = true)
public FeverResponse get(@Context UriInfo uri, @PathParam("userId") Long userId) {
Map<String, String> params = new HashMap<>();
uri.getQueryParameters().forEach((k, v) -> params.put(k, v.get(0)));
uri.getQueryParameters().forEach((k, v) -> params.put(k, v.getFirst()));
return handle(userId, params);
}
@@ -132,7 +132,7 @@ public class FeverREST {
@Operation(hidden = true)
public FeverResponse formData(@Context UriInfo uri, @PathParam("userId") Long userId, MultipartFormDataInput form) {
Map<String, String> params = new HashMap<>();
uri.getQueryParameters().forEach((k, v) -> params.put(k, v.get(0)));
uri.getQueryParameters().forEach((k, v) -> params.put(k, v.getFirst()));
form.getValues().forEach((k, v) -> params.put(k, v.stream().map(FormValue::getValue).findFirst().orElse(null)));
return handle(userId, params);
}

View File

@@ -1,8 +1,5 @@
package com.commafeed.frontend.servlet;
import java.time.Instant;
import java.util.Date;
import jakarta.annotation.security.PermitAll;
import jakarta.inject.Singleton;
import jakarta.ws.rs.GET;
@@ -11,26 +8,25 @@ import jakarta.ws.rs.core.NewCookie;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriInfo;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.openapi.annotations.Operation;
import com.commafeed.security.CookieService;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Path("/logout")
@PermitAll
@Singleton
public class LogoutServlet {
private final UriInfo uri;
private final String cookieName;
public LogoutServlet(UriInfo uri, @ConfigProperty(name = "quarkus.http.auth.form.cookie-name") String cookieName) {
this.uri = uri;
this.cookieName = cookieName;
}
private final CookieService cookieService;
@GET
@Operation(hidden = true)
public Response get() {
NewCookie removeCookie = new NewCookie.Builder(cookieName).maxAge(0).expiry(Date.from(Instant.EPOCH)).path("/").build();
NewCookie removeCookie = cookieService.buildLogoutCookie();
return Response.temporaryRedirect(uri.getBaseUri()).cookie(removeCookie).build();
}
}

View File

@@ -0,0 +1,24 @@
package com.commafeed.security;
import java.time.Instant;
import java.util.Date;
import jakarta.inject.Singleton;
import jakarta.ws.rs.core.NewCookie;
import io.quarkus.vertx.http.runtime.VertxHttpConfig;
@Singleton
public class CookieService {
private final String cookieName;
public CookieService(VertxHttpConfig config) {
this.cookieName = config.auth().form().cookieName();
}
public NewCookie buildLogoutCookie() {
return new NewCookie.Builder(cookieName).maxAge(0).expiry(Date.from(Instant.EPOCH)).path("/").build();
}
}

View File

@@ -6,8 +6,6 @@ import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.apache.commons.lang3.StringUtils;
import org.passay.CharacterRule;
import org.passay.EnglishCharacterData;
import org.passay.LengthRule;
import org.passay.PasswordData;
import org.passay.PasswordValidator;
@@ -19,7 +17,7 @@ import lombok.Setter;
public class PasswordConstraintValidator implements ConstraintValidator<ValidPassword, String> {
@Setter
private static boolean strict = true;
private static int minimumPasswordLength;
@Override
public void initialize(ValidPassword constraintAnnotation) {
@@ -32,7 +30,7 @@ public class PasswordConstraintValidator implements ConstraintValidator<ValidPas
return true;
}
PasswordValidator validator = strict ? buildStrictPasswordValidator() : buildLoosePasswordValidator();
PasswordValidator validator = buildPasswordValidator();
RuleResult result = validator.validate(new PasswordData(value));
if (result.isValid()) {
@@ -45,28 +43,11 @@ public class PasswordConstraintValidator implements ConstraintValidator<ValidPas
return false;
}
private PasswordValidator buildStrictPasswordValidator() {
private PasswordValidator buildPasswordValidator() {
return new PasswordValidator(
// length
new LengthRule(8, 256),
// 1 uppercase char
new CharacterRule(EnglishCharacterData.UpperCase, 1),
// 1 lowercase char
new CharacterRule(EnglishCharacterData.LowerCase, 1),
// 1 digit
new CharacterRule(EnglishCharacterData.Digit, 1),
// 1 special char
new CharacterRule(EnglishCharacterData.Special, 1),
new LengthRule(minimumPasswordLength, 256),
// no whitespace
new WhitespaceRule());
}
private PasswordValidator buildLoosePasswordValidator() {
return new PasswordValidator(
// length
new LengthRule(6, 256),
// no whitespace
new WhitespaceRule());
}
}

View File

@@ -6,8 +6,6 @@ import jakarta.persistence.EntityManager;
import org.hibernate.Session;
import org.kohsuke.MetaInfServices;
import com.commafeed.backend.service.db.DatabaseStartupService;
import io.quarkus.test.junit.callback.QuarkusTestBeforeEachCallback;
import io.quarkus.test.junit.callback.QuarkusTestMethodContext;
@@ -26,7 +24,5 @@ public class DatabaseReset implements QuarkusTestBeforeEachCallback {
.getSessionFactory()
.getSchemaManager()
.truncateMappedObjects();
CDI.current().select(DatabaseStartupService.class).get().populateInitialData();
}
}

View File

@@ -0,0 +1,6 @@
package com.commafeed;
public class TestConstants {
public static final String ADMIN_USERNAME = "admin";
public static final String ADMIN_PASSWORD = "admin";
}

View File

@@ -31,10 +31,10 @@ class FeedUtilsTest {
Assertions.assertEquals("Test Entry", syndEntry.getTitle());
Assertions.assertEquals("Author Name", syndEntry.getAuthor());
Assertions.assertEquals(1, syndEntry.getContents().size());
Assertions.assertEquals("This is a test entry content.", syndEntry.getContents().get(0).getValue());
Assertions.assertEquals("This is a test entry content.", syndEntry.getContents().getFirst().getValue());
Assertions.assertEquals(1, syndEntry.getEnclosures().size());
Assertions.assertEquals("http://example.com/enclosure.mp3", syndEntry.getEnclosures().get(0).getUrl());
Assertions.assertEquals("audio/mpeg", syndEntry.getEnclosures().get(0).getType());
Assertions.assertEquals("http://example.com/enclosure.mp3", syndEntry.getEnclosures().getFirst().getUrl());
Assertions.assertEquals("audio/mpeg", syndEntry.getEnclosures().getFirst().getType());
Assertions.assertEquals("http://example.com/test-entry", syndEntry.getLink());
Assertions.assertEquals(Date.from(Instant.ofEpochSecond(1)), syndEntry.getPublishedDate());
}

View File

@@ -188,7 +188,7 @@ class UserServiceTest {
void apiLoginShouldReturnUserIfUserFoundFromApikeyLookupNotDisabled() {
Mockito.when(userDAO.findByApiKey("apikey")).thenReturn(normalUser);
Optional<User> returnedUser = userService.login("apikey");
Assertions.assertEquals(normalUser, returnedUser.get());
Assertions.assertEquals(Optional.of(normalUser), returnedUser);
}
}

View File

@@ -1,8 +1,10 @@
package com.commafeed.e2e;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.commafeed.TestConstants;
import com.microsoft.playwright.BrowserContext;
import com.microsoft.playwright.Locator;
import com.microsoft.playwright.Page;
@@ -20,6 +22,11 @@ class AuthentificationIT {
@InjectPlaywright
private BrowserContext context;
@BeforeEach
void setup() {
PlaywrightTestUtils.initialSetup();
}
@AfterEach
void cleanup() {
context.clearCookies();
@@ -29,7 +36,7 @@ class AuthentificationIT {
void loginFail() {
Page page = context.newPage();
page.navigate(getLoginPageUrl());
PlaywrightTestUtils.login(page, "admin", "wrong_password");
PlaywrightTestUtils.login(page, TestConstants.ADMIN_USERNAME, "wrong_password");
PlaywrightAssertions.assertThat(page.getByRole(AriaRole.ALERT)).containsText("wrong username or password");
}
@@ -46,13 +53,10 @@ class AuthentificationIT {
Page page = context.newPage();
page.navigate(getLoginPageUrl());
page.getByText("Sign up!").click();
PlaywrightTestUtils.register(page, "user", "user@domain.com", "pass");
PlaywrightTestUtils.register(page, "user", "user@domain.com", "p");
Locator alert = page.getByRole(AriaRole.ALERT);
PlaywrightAssertions.assertThat(alert).containsText("Password must be 8 or more characters in length.");
PlaywrightAssertions.assertThat(alert).containsText("Password must contain 1 or more uppercase characters.");
PlaywrightAssertions.assertThat(alert).containsText("Password must contain 1 or more digit characters.");
PlaywrightAssertions.assertThat(alert).containsText("Password must contain 1 or more special characters.");
PlaywrightAssertions.assertThat(alert).containsText("Password must be 4 or more characters in length.");
}
@Test

View File

@@ -0,0 +1,33 @@
package com.commafeed.e2e;
import org.junit.jupiter.api.Test;
import com.commafeed.TestConstants;
import com.microsoft.playwright.BrowserContext;
import com.microsoft.playwright.Page;
import com.microsoft.playwright.assertions.PlaywrightAssertions;
import com.microsoft.playwright.options.AriaRole;
import io.quarkiverse.playwright.InjectPlaywright;
import io.quarkiverse.playwright.WithPlaywright;
import io.quarkus.test.junit.QuarkusTest;
@QuarkusTest
@WithPlaywright
class InitialSetupIT {
@InjectPlaywright
private BrowserContext context;
@Test
void createAdminAccount() {
Page page = context.newPage();
page.navigate("http://localhost:8085");
page.getByPlaceholder("Admin User Name").fill(TestConstants.ADMIN_USERNAME);
page.getByPlaceholder("Password").fill(TestConstants.ADMIN_PASSWORD);
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Create Admin Account")).click();
PlaywrightAssertions.assertThat(page).hasURL("http://localhost:8085/#/app/category/all");
}
}

View File

@@ -1,16 +1,28 @@
package com.commafeed.e2e;
import com.commafeed.TestConstants;
import com.commafeed.frontend.model.request.InitialSetupRequest;
import com.microsoft.playwright.Page;
import com.microsoft.playwright.Page.GetByRoleOptions;
import com.microsoft.playwright.options.AriaRole;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import lombok.experimental.UtilityClass;
@UtilityClass
public class PlaywrightTestUtils {
public static void initialSetup() {
InitialSetupRequest req = new InitialSetupRequest();
req.setName(TestConstants.ADMIN_USERNAME);
req.setPassword(TestConstants.ADMIN_PASSWORD);
RestAssured.given().body(req).contentType(ContentType.JSON).post("rest/user/initialSetup").then().statusCode(200);
}
public static void login(Page page) {
login(page, "admin", "admin");
login(page, TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
}
public static void login(Page page, String username, String password) {

View File

@@ -16,6 +16,7 @@ import org.mockserver.integration.ClientAndServer;
import org.mockserver.model.HttpRequest;
import org.mockserver.model.HttpResponse;
import com.commafeed.TestConstants;
import com.commafeed.frontend.model.Entries;
import com.microsoft.playwright.BrowserContext;
import com.microsoft.playwright.Locator;
@@ -45,7 +46,8 @@ class ReadingIT {
.withBody(IOUtils.toString(getClass().getResource("/feed/rss.xml"), StandardCharsets.UTF_8))
.withDelay(TimeUnit.MILLISECONDS, 100));
RestAssured.authentication = RestAssured.preemptive().basic("admin", "admin");
PlaywrightTestUtils.initialSetup();
RestAssured.authentication = RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
}
@AfterEach

View File

@@ -21,10 +21,12 @@ import org.mockserver.integration.ClientAndServer;
import org.mockserver.model.HttpRequest;
import org.mockserver.model.HttpResponse;
import com.commafeed.TestConstants;
import com.commafeed.frontend.model.Category;
import com.commafeed.frontend.model.Entries;
import com.commafeed.frontend.model.Subscription;
import com.commafeed.frontend.model.request.AddCategoryRequest;
import com.commafeed.frontend.model.request.InitialSetupRequest;
import com.commafeed.frontend.model.request.SubscribeRequest;
import io.restassured.RestAssured;
@@ -76,11 +78,20 @@ public abstract class BaseIT {
mockServerClient.when(FEED_REQUEST).respond(HttpResponse.response().withBody(IOUtils.toString(resource, StandardCharsets.UTF_8)));
}
protected void initialSetup(String userName, String password) {
InitialSetupRequest req = new InitialSetupRequest();
req.setName(userName);
req.setPassword(password);
req.setEmail(userName + "@commafeed.com");
RestAssured.given().body(req).contentType(ContentType.JSON).post("rest/user/initialSetup").then().statusCode(200);
}
protected List<HttpCookie> login() {
List<Header> setCookieHeaders = RestAssured.given()
.auth()
.none()
.formParams("j_username", "admin", "j_password", "admin")
.formParams("j_username", TestConstants.ADMIN_USERNAME, "j_password", TestConstants.ADMIN_PASSWORD)
.post("j_security_check")
.then()
.statusCode(HttpStatus.SC_OK)

View File

@@ -8,9 +8,11 @@ import jakarta.ws.rs.core.HttpHeaders;
import org.apache.hc.core5.http.HttpStatus;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.commafeed.ExceptionMappers.UnauthorizedResponse;
import com.commafeed.TestConstants;
import com.commafeed.frontend.model.Entries;
import com.commafeed.frontend.model.UserModel;
import com.commafeed.frontend.model.request.MarkRequest;
@@ -24,6 +26,11 @@ import io.restassured.http.ContentType;
@QuarkusTest
class SecurityIT extends BaseIT {
@BeforeEach
void setup() {
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
}
@Test
void notLoggedIn() {
UnauthorizedResponse info = RestAssured.given()
@@ -49,7 +56,13 @@ class SecurityIT extends BaseIT {
@Test
void basicAuthLogin() {
RestAssured.given().auth().preemptive().basic("admin", "admin").get("rest/user/profile").then().statusCode(HttpStatus.SC_OK);
RestAssured.given()
.auth()
.preemptive()
.basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD)
.get("rest/user/profile")
.then()
.statusCode(HttpStatus.SC_OK);
}
@Test
@@ -57,7 +70,7 @@ class SecurityIT extends BaseIT {
RestAssured.given()
.auth()
.preemptive()
.basic("admin", "wrong-password")
.basic(TestConstants.ADMIN_USERNAME, "wrong-password")
.get("rest/user/profile")
.then()
.statusCode(HttpStatus.SC_UNAUTHORIZED);
@@ -72,12 +85,12 @@ class SecurityIT extends BaseIT {
void apiKey() {
// create api key
ProfileModificationRequest req = new ProfileModificationRequest();
req.setCurrentPassword("admin");
req.setCurrentPassword(TestConstants.ADMIN_PASSWORD);
req.setNewApiKey(true);
RestAssured.given()
.auth()
.preemptive()
.basic("admin", "admin")
.basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD)
.body(req)
.contentType(ContentType.JSON)
.post("rest/user/profile")
@@ -88,7 +101,7 @@ class SecurityIT extends BaseIT {
String apiKey = RestAssured.given()
.auth()
.preemptive()
.basic("admin", "admin")
.basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD)
.get("rest/user/profile")
.then()
.statusCode(HttpStatus.SC_OK)
@@ -103,7 +116,7 @@ class SecurityIT extends BaseIT {
long subscriptionId = RestAssured.given()
.auth()
.preemptive()
.basic("admin", "admin")
.basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD)
.body(subscribeRequest)
.contentType(ContentType.JSON)
.post("rest/feed/subscribe")

View File

@@ -27,6 +27,7 @@ import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.commafeed.TestConstants;
import com.commafeed.frontend.model.request.FeedModificationRequest;
import io.quarkus.test.junit.QuarkusTest;
@@ -40,7 +41,8 @@ class WebSocketIT extends BaseIT {
@BeforeEach
void setup() {
RestAssured.authentication = RestAssured.preemptive().basic("admin", "admin");
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
RestAssured.authentication = RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
}
@AfterEach

View File

@@ -9,6 +9,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import com.commafeed.TestConstants;
import com.commafeed.frontend.model.UserModel;
import com.commafeed.frontend.model.request.AdminSaveUserRequest;
import com.commafeed.frontend.model.request.IDRequest;
@@ -23,7 +24,8 @@ class AdminIT extends BaseIT {
@BeforeEach
void setup() {
RestAssured.authentication = RestAssured.preemptive().basic("admin", "admin");
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
RestAssured.authentication = RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
}
@AfterEach

View File

@@ -11,6 +11,7 @@ import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.xml.sax.InputSource;
import com.commafeed.TestConstants;
import com.commafeed.frontend.model.Category;
import com.commafeed.frontend.model.Entries;
import com.commafeed.frontend.model.Entry;
@@ -35,7 +36,8 @@ import io.restassured.http.ContentType;
class CategoryIT extends BaseIT {
@BeforeEach
void setup() {
RestAssured.authentication = RestAssured.preemptive().basic("admin", "admin");
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
RestAssured.authentication = RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
}
@AfterEach
@@ -58,9 +60,9 @@ class CategoryIT extends BaseIT {
Category root = getRootCategory();
Assertions.assertEquals(2, root.getChildren().size());
Assertions.assertEquals("test-category-1", root.getChildren().get(0).getName());
Assertions.assertEquals(1, root.getChildren().get(0).getChildren().size());
Assertions.assertEquals("modified-category-2", root.getChildren().get(0).getChildren().get(0).getName());
Assertions.assertEquals("test-category-1", root.getChildren().getFirst().getName());
Assertions.assertEquals(1, root.getChildren().getFirst().getChildren().size());
Assertions.assertEquals("modified-category-2", root.getChildren().getFirst().getChildren().getFirst().getName());
request = new CategoryModificationRequest();
request.setId(Long.valueOf(category3Id));
@@ -79,7 +81,7 @@ class CategoryIT extends BaseIT {
Category root = getRootCategory();
Assertions.assertEquals(1, root.getChildren().size());
Assertions.assertTrue(root.getChildren().get(0).isExpanded());
Assertions.assertTrue(root.getChildren().getFirst().isExpanded());
CollapseRequest request = new CollapseRequest();
request.setId(Long.valueOf(categoryId));
@@ -88,7 +90,7 @@ class CategoryIT extends BaseIT {
root = getRootCategory();
Assertions.assertEquals(1, root.getChildren().size());
Assertions.assertFalse(root.getChildren().get(0).isExpanded());
Assertions.assertFalse(root.getChildren().getFirst().isExpanded());
}
@Test
@@ -178,7 +180,7 @@ class CategoryIT extends BaseIT {
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
Assertions.assertEquals(0, getCategoryEntries(CategoryREST.STARRED).getEntries().size());
Entry entry = getFeedEntries(subscriptionId).getEntries().get(0);
Entry entry = getFeedEntries(subscriptionId).getEntries().getFirst();
StarRequest starRequest = new StarRequest();
starRequest.setId(entry.getId());
@@ -188,7 +190,7 @@ class CategoryIT extends BaseIT {
Entries starredEntries = getCategoryEntries(CategoryREST.STARRED);
Assertions.assertEquals(1, starredEntries.getEntries().size());
Assertions.assertEquals(entry.getId(), starredEntries.getEntries().get(0).getId());
Assertions.assertEquals(entry.getId(), starredEntries.getEntries().getFirst().getId());
}
@Test
@@ -196,7 +198,7 @@ class CategoryIT extends BaseIT {
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
Assertions.assertEquals(0, getTaggedEntries("my-tag").getEntries().size());
Entry entry = getFeedEntries(subscriptionId).getEntries().get(0);
Entry entry = getFeedEntries(subscriptionId).getEntries().getFirst();
TagRequest tagRequest = new TagRequest();
tagRequest.setEntryId(Long.valueOf(entry.getId()));
@@ -205,7 +207,7 @@ class CategoryIT extends BaseIT {
Entries taggedEntries = getTaggedEntries("my-tag");
Assertions.assertEquals(1, taggedEntries.getEntries().size());
Assertions.assertEquals(entry.getId(), taggedEntries.getEntries().get(0).getId());
Assertions.assertEquals(entry.getId(), taggedEntries.getEntries().getFirst().getId());
}
@Test

View File

@@ -22,6 +22,7 @@ import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.xml.sax.InputSource;
import com.commafeed.TestConstants;
import com.commafeed.frontend.model.Entry;
import com.commafeed.frontend.model.FeedInfo;
import com.commafeed.frontend.model.Subscription;
@@ -43,7 +44,8 @@ class FeedIT extends BaseIT {
@BeforeEach
void setup() {
RestAssured.authentication = RestAssured.preemptive().basic("admin", "admin");
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
RestAssured.authentication = RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
}
@AfterEach

View File

@@ -6,6 +6,7 @@ import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.commafeed.TestConstants;
import com.commafeed.backend.Digests;
import com.commafeed.frontend.model.Entry;
import com.commafeed.frontend.model.UserModel;
@@ -28,11 +29,12 @@ class FeverIT extends BaseIT {
@BeforeEach
void setup() {
RestAssured.authentication = RestAssured.preemptive().basic("admin", "admin");
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
RestAssured.authentication = RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
// create api key
ProfileModificationRequest req = new ProfileModificationRequest();
req.setCurrentPassword("admin");
req.setCurrentPassword(TestConstants.ADMIN_PASSWORD);
req.setNewApiKey(true);
RestAssured.given().body(req).contentType(ContentType.JSON).post("rest/user/profile").then().statusCode(HttpStatus.SC_OK);
@@ -80,7 +82,7 @@ class FeverIT extends BaseIT {
FeverResponse feverResponse = client.execute("items");
Assertions.assertEquals(2, feverResponse.getItems().size());
FeverItem item = feverResponse.getItems().get(0);
FeverItem item = feverResponse.getItems().getFirst();
Assertions.assertEquals(subscriptionId, item.getFeedId());
Assertions.assertEquals("Item 2", item.getTitle());
Assertions.assertEquals("Item 2 description", item.getHtml());
@@ -92,7 +94,7 @@ class FeverIT extends BaseIT {
@Test
void entriesByIds() {
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
Entry entry = getFeedEntries(subscriptionId).getEntries().get(0);
Entry entry = getFeedEntries(subscriptionId).getEntries().getFirst();
FeverResponse feverResponse = client.execute("items", new Param("with_ids", entry.getId()));
Assertions.assertEquals(1, feverResponse.getItems().size());
@@ -101,7 +103,7 @@ class FeverIT extends BaseIT {
@Test
void savedEntries() {
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
Entry entry = getFeedEntries(subscriptionId).getEntries().get(0);
Entry entry = getFeedEntries(subscriptionId).getEntries().getFirst();
StarRequest starRequest = new StarRequest();
starRequest.setId(entry.getId());
@@ -116,7 +118,7 @@ class FeverIT extends BaseIT {
@Test
void markEntry() {
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
Entry entry = getFeedEntries(subscriptionId).getEntries().get(0);
Entry entry = getFeedEntries(subscriptionId).getEntries().getFirst();
client.execute("_", new Param("mark", "item"), new Param("id", entry.getId()), new Param("as", "read"));
Assertions.assertEquals(1, getFeedEntries(subscriptionId).getEntries().stream().filter(Entry::isRead).count());
@@ -147,7 +149,7 @@ class FeverIT extends BaseIT {
@Test
void tagEntry() {
Long subscriptionId = subscribeAndWaitForEntries(getFeedUrl());
Entry entry = getFeedEntries(subscriptionId).getEntries().get(0);
Entry entry = getFeedEntries(subscriptionId).getEntries().getFirst();
client.execute("_", new Param("mark", "item"), new Param("id", entry.getId()), new Param("as", "saved"));
Assertions.assertEquals(1, getFeedEntries(subscriptionId).getEntries().stream().filter(Entry::isStarred).count());
@@ -161,7 +163,7 @@ class FeverIT extends BaseIT {
createCategory("category-1");
FeverResponse feverResponse = client.execute("groups");
Assertions.assertEquals(1, feverResponse.getGroups().size());
Assertions.assertEquals("category-1", feverResponse.getGroups().get(0).getTitle());
Assertions.assertEquals("category-1", feverResponse.getGroups().getFirst().getTitle());
}
@Test

View File

@@ -11,6 +11,7 @@ import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.commafeed.TestConstants;
import com.commafeed.frontend.model.Settings;
import com.commafeed.frontend.model.request.PasswordResetRequest;
import com.commafeed.integration.BaseIT;
@@ -29,7 +30,8 @@ class UserIT extends BaseIT {
@BeforeEach
void setup() {
RestAssured.authentication = RestAssured.preemptive().basic("admin", "admin");
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
RestAssured.authentication = RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
mailbox.clear();
}
@@ -48,12 +50,12 @@ class UserIT extends BaseIT {
List<MailMessage> mails = mailbox.getMailMessagesSentTo("admin@commafeed.com");
Assertions.assertEquals(1, mails.size());
MailMessage message = mails.get(0);
MailMessage message = mails.getFirst();
Assertions.assertEquals("CommaFeed - Password recovery", message.getSubject());
Assertions.assertTrue(message.getHtml().startsWith("You asked for password recovery for account 'admin'"));
Assertions.assertEquals("admin@commafeed.com", message.getTo().get(0));
Assertions.assertEquals("admin@commafeed.com", message.getTo().getFirst());
Element a = Jsoup.parse(message.getHtml()).select("a").get(0);
Element a = Jsoup.parse(message.getHtml()).select("a").getFirst();
String link = a.attr("href");
String newPasswordResponse = RestAssured.given().urlEncodingEnabled(false).get(link).then().statusCode(200).extract().asString();
Assertions.assertTrue(newPasswordResponse.contains("Your new password is:"));

View File

@@ -6,6 +6,7 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.commafeed.TestConstants;
import com.commafeed.frontend.model.Settings;
import com.commafeed.integration.BaseIT;
@@ -18,7 +19,8 @@ class CustomCodeIT extends BaseIT {
@BeforeEach
void setup() {
RestAssured.authentication = RestAssured.preemptive().basic("admin", "admin");
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
RestAssured.authentication = RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
}
@AfterEach

View File

@@ -8,8 +8,10 @@ import jakarta.ws.rs.core.HttpHeaders;
import org.apache.hc.core5.http.HttpStatus;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.commafeed.TestConstants;
import com.commafeed.integration.BaseIT;
import io.quarkus.test.junit.QuarkusTest;
@@ -19,6 +21,11 @@ import io.restassured.http.Headers;
@QuarkusTest
class LogoutIT extends BaseIT {
@BeforeEach
void setup() {
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
}
@Test
void test() {
List<HttpCookie> cookies = login();

View File

@@ -7,6 +7,7 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.commafeed.TestConstants;
import com.commafeed.integration.BaseIT;
import io.quarkus.test.junit.QuarkusTest;
@@ -17,7 +18,8 @@ class NextUnreadIT extends BaseIT {
@BeforeEach
void setup() {
RestAssured.authentication = RestAssured.preemptive().basic("admin", "admin");
initialSetup(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
RestAssured.authentication = RestAssured.preemptive().basic(TestConstants.ADMIN_USERNAME, TestConstants.ADMIN_PASSWORD);
}
@AfterEach

View File

@@ -5,13 +5,13 @@
<groupId>com.commafeed</groupId>
<artifactId>commafeed</artifactId>
<version>5.12.1</version>
<version>6.0.0</version>
<name>CommaFeed</name>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.release>17</maven.compiler.release>
<maven.compiler.release>25</maven.compiler.release>
</properties>
<build>