Compare commits

...

145 Commits

Author SHA1 Message Date
Elias Schneider
ac9b0a1d53 release: 1.6.1 2024-11-26 15:53:33 +01:00
Elias Schneider
ba2e7e122c fix: error for non oidc oauth clients 2024-11-26 15:53:01 +01:00
Elias Schneider
3527dd1dd9 release: 1.6.0 2024-11-25 12:44:42 +01:00
Elias Schneider
54af6c2055 chore(translations): update translations via Crowdin (#684)
* New translations en-us.ts (Spanish)

* New translations en-us.ts (Hungarian)

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Thai)

* New translations en-us.ts (French)

* New translations en-us.ts (Russian)

* New translations en-us.ts (Chinese Simplified)

* New translations en-us.ts (Bulgarian)

* New translations en-us.ts (Czech)

* New translations en-us.ts (Danish)

* New translations en-us.ts (German)

* New translations en-us.ts (Greek)

* New translations en-us.ts (Finnish)

* New translations en-us.ts (Italian)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Korean)

* New translations en-us.ts (Polish)

* New translations en-us.ts (Slovenian)

* New translations en-us.ts (Swedish)

* New translations en-us.ts (Turkish)

* New translations en-us.ts (Ukrainian)

* New translations en-us.ts (Chinese Traditional)

* New translations en-us.ts (Vietnamese)

* New translations en-us.ts (Serbian (Latin))

* New translations en-us.ts (Dutch, Belgium)

* New translations en-us.ts (Arabic, Egypt)

* New translations en-us.ts (Italian)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Ukrainian)

* New translations en-us.ts (Ukrainian)

* New translations en-us.ts (Chinese Traditional)

* New translations en-us.ts (Chinese Traditional)

* New translations en-us.ts (Spanish)

* New translations en-us.ts (Hungarian)

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (French)

* New translations en-us.ts (Russian)

* New translations en-us.ts (Bulgarian)

* New translations en-us.ts (Czech)

* New translations en-us.ts (Danish)

* New translations en-us.ts (German)

* New translations en-us.ts (Greek)

* New translations en-us.ts (Finnish)

* New translations en-us.ts (Italian)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Korean)

* New translations en-us.ts (Polish)

* New translations en-us.ts (Slovenian)

* New translations en-us.ts (Swedish)

* New translations en-us.ts (Turkish)

* New translations en-us.ts (Ukrainian)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Thai)

* New translations en-us.ts (Chinese Simplified)

* New translations en-us.ts (Chinese Traditional)

* New translations en-us.ts (Vietnamese)

* New translations en-us.ts (Serbian (Latin))

* New translations en-us.ts (Dutch, Belgium)

* New translations en-us.ts (Arabic, Egypt)
2024-11-25 12:44:33 +01:00
Elias Schneider
3160f90e1d fix: add validation for share id and zip compression config variables 2024-11-25 12:41:03 +01:00
Elias Schneider
da54ce6ee0 feat: add config variable to specify the requested OIDC sopes 2024-11-25 12:21:17 +01:00
Elias Schneider
468b25828b release: 1.5.0 2024-11-24 17:26:14 +01:00
Romain Ricard
9d4bb55a09 feat(share): add share ID length setting (#677)
- Add share ID length to share > settings
- Use cryptographically secure RNG for IDs
- Use secure default value for IDs length
- Add FR and EN translation

Co-authored-by: Romain Ricard <romain.ricard@mines-ales.org>
2024-11-24 17:25:50 +01:00
Elias Schneider
f78ffd69e7 chore(translations): update translations via Crowdin (#681)
* New translations en-us.ts (German)

* New translations en-us.ts (German)

* New translations en-us.ts (Ukrainian)

* New translations en-us.ts (Ukrainian)

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (Serbian (Latin))
2024-11-24 17:24:10 +01:00
Elias Schneider
17528f999a Merge branch 'main' of https://github.com/stonith404/pingvin-share 2024-11-23 18:55:53 +01:00
Elias Schneider
c8f05f2475 fix: totp can't be enabled if user is a ldap user 2024-11-23 18:55:47 +01:00
Elias Schneider
424e2564d5 chore(translations): update translations via Crowdin (#672)
* New translations en-us.ts (Italian)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (French)

* New translations en-us.ts (French)

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (Serbian (Latin))

* New translations en-us.ts (Spanish)

* New translations en-us.ts (Hungarian)

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Thai)

* New translations en-us.ts (French)

* New translations en-us.ts (Russian)

* New translations en-us.ts (Chinese Simplified)

* New translations en-us.ts (Bulgarian)

* New translations en-us.ts (Czech)

* New translations en-us.ts (Danish)

* New translations en-us.ts (German)

* New translations en-us.ts (Greek)

* New translations en-us.ts (Finnish)

* New translations en-us.ts (Italian)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Korean)

* New translations en-us.ts (Polish)

* New translations en-us.ts (Slovenian)

* New translations en-us.ts (Swedish)

* New translations en-us.ts (Turkish)

* New translations en-us.ts (Ukrainian)

* New translations en-us.ts (Chinese Traditional)

* New translations en-us.ts (Vietnamese)

* New translations en-us.ts (Serbian (Latin))

* New translations en-us.ts (Dutch, Belgium)

* New translations en-us.ts (Arabic, Egypt)

* New translations en-us.ts (Italian)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (French)

* New translations en-us.ts (German)

* New translations en-us.ts (German)

* New translations en-us.ts (Dutch, Belgium)

* New translations en-us.ts (Dutch, Belgium)

* New translations en-us.ts (German)

* New translations en-us.ts (German)
2024-11-21 20:51:45 +01:00
Elias Schneider
18d8cbbbab release: 1.4.0 2024-11-17 16:22:29 +01:00
Elias Schneider
c7dacb26e8 feat: add "creatorEmail" config bariable to share recipient email message 2024-11-17 16:11:30 +01:00
Elias Schneider
b6d98c7c42 chore: upgrade vulnerable dependencies 2024-11-17 16:09:40 +01:00
Elias Schneider
c52ec71920 refactor: use fs promises in file service 2024-11-17 16:09:23 +01:00
Elias Schneider
6cf5c66fe2 fix: remote arbitrary file overwrite on file upload endpoint 2024-11-17 16:07:21 +01:00
Elias Schneider
51478b6a9f release: 1.3.0 2024-11-14 19:10:37 +01:00
Elias Schneider
6f45c3b1fb fix: use current window url instead of app url in frontend 2024-11-14 19:10:11 +01:00
Elias Schneider
ff2dd81055 chore(translations): update translations via Crowdin (#665)
* New translations en-us.ts (Spanish)

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Thai)

* New translations en-us.ts (Thai)

* New translations en-us.ts (Hungarian)

* New translations en-us.ts (Hungarian)

* New translations en-us.ts (French)

* New translations en-us.ts (French)

* New translations en-us.ts (Russian)

* New translations en-us.ts (Chinese Simplified)

* New translations en-us.ts (French)

* New translations en-us.ts (Spanish)

* New translations en-us.ts (Hungarian)

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Thai)

* New translations en-us.ts (French)

* New translations en-us.ts (Russian)

* New translations en-us.ts (Chinese Simplified)

* New translations en-us.ts (Bulgarian)

* New translations en-us.ts (Czech)

* New translations en-us.ts (Danish)

* New translations en-us.ts (German)

* New translations en-us.ts (Greek)

* New translations en-us.ts (Finnish)

* New translations en-us.ts (Italian)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Korean)

* New translations en-us.ts (Polish)

* New translations en-us.ts (Slovenian)

* New translations en-us.ts (Swedish)

* New translations en-us.ts (Turkish)

* New translations en-us.ts (Ukrainian)

* New translations en-us.ts (Chinese Traditional)

* New translations en-us.ts (Vietnamese)

* New translations en-us.ts (Serbian (Latin))

* New translations en-us.ts (Dutch, Belgium)

* New translations en-us.ts (Arabic, Egypt)

* New translations en-us.ts (Spanish)

* New translations en-us.ts (Hungarian)

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Thai)

* New translations en-us.ts (French)

* New translations en-us.ts (Russian)

* New translations en-us.ts (Chinese Simplified)

* New translations en-us.ts (Bulgarian)

* New translations en-us.ts (Czech)

* New translations en-us.ts (Danish)

* New translations en-us.ts (German)

* New translations en-us.ts (Greek)

* New translations en-us.ts (Finnish)

* New translations en-us.ts (Italian)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Korean)

* New translations en-us.ts (Polish)

* New translations en-us.ts (Slovenian)

* New translations en-us.ts (Swedish)

* New translations en-us.ts (Turkish)

* New translations en-us.ts (Ukrainian)

* New translations en-us.ts (Chinese Traditional)

* New translations en-us.ts (Vietnamese)

* New translations en-us.ts (Serbian (Latin))

* New translations en-us.ts (Dutch, Belgium)

* New translations en-us.ts (Arabic, Egypt)
2024-11-14 18:57:20 +01:00
Elias Schneider
c26de4e881 fix: throw error if no disk space is left 2024-11-14 18:44:32 +01:00
Elias Schneider
4ef7ebb062 fix: improve share completed dialog redirection for reverse shares 2024-11-14 18:41:34 +01:00
Elias Schneider
d870b5721a refactor: run formatter 2024-11-14 18:04:18 +01:00
Elias Schneider
d8084e401d feat: add confirm dialog for leaving the page if an upload is in progress 2024-11-14 18:04:11 +01:00
Elias Schneider
e1a5d19544 fix: prevent deletion of last admin account 2024-11-14 17:39:06 +01:00
Elias Schneider
4ce64206be feat: add 'secureCookies' configuration variable to explicitly set the secure flag and prevent confusion 2024-11-14 17:31:17 +01:00
Elias Schneider
77eef187b7 chore(translations): fix wrong label for Serbian latin and cyrillic 2024-10-31 17:01:03 +01:00
Elias Schneider
c7138bcf5d chore(translations): update translations via Crowdin (#657)
* New translations en-us.ts (Serbian (Latin))

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (German)

* New translations en-us.ts (French)

* New translations en-us.ts (French)

* New translations en-us.ts (German)

* New translations en-us.ts (German)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (German)

* New translations en-us.ts (Spanish)
2024-10-31 16:57:43 +01:00
Elias Schneider
ab4f19e921 release: 1.2.4 2024-10-24 14:45:05 +02:00
Elias Schneider
428c1d2b99 fix: don't enforce password lenght for sign in form because of LDAP 2024-10-24 14:44:07 +02:00
Elias Schneider
c89ca7e64b fix: use app name as totp issuer 2024-10-24 13:59:54 +02:00
Elias Schneider
297e8c0ab1 release: 1.2.3 2024-10-23 15:49:09 +02:00
Elias Schneider
446f9dd209 refactor: run formatter 2024-10-23 15:48:55 +02:00
Elias Schneider
acbff6e129 fix: share password can be bypassed if a deleted share with the same id was visited before 2024-10-23 15:48:47 +02:00
Elias Schneider
546d2c1ce4 chore(translations): add Serbian Cyrillic language files 2024-10-23 14:10:29 +02:00
Elias Schneider
37839e6b18 chore(translations): update translations via Crowdin (#644)
* New translations en-us.ts (French)

* New translations en-us.ts (German)

* New translations en-us.ts (Polish)

* New translations en-us.ts (French)

* New translations en-us.ts (German)

* New translations en-us.ts (German)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (French)

* New translations en-us.ts (Danish)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Vietnamese)

* New translations en-us.ts (Spanish)

* New translations en-us.ts (Bulgarian)

* New translations en-us.ts (Czech)

* New translations en-us.ts (German)

* New translations en-us.ts (Greek)

* New translations en-us.ts (Finnish)

* New translations en-us.ts (Hungarian)

* New translations en-us.ts (Italian)

* New translations en-us.ts (Korean)

* New translations en-us.ts (Polish)

* New translations en-us.ts (Russian)

* New translations en-us.ts (Slovenian)

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (Swedish)

* New translations en-us.ts (Turkish)

* New translations en-us.ts (Ukrainian)

* New translations en-us.ts (Chinese Simplified)

* New translations en-us.ts (Chinese Traditional)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Thai)

* New translations en-us.ts (Dutch, Belgium)

* New translations en-us.ts (Arabic, Egypt)

* New translations en-us.ts (French)

* New translations en-us.ts (German)

* New translations en-us.ts (Italian)

* New translations en-us.ts (French)

* New translations en-us.ts (Danish)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Vietnamese)

* New translations en-us.ts (Spanish)

* New translations en-us.ts (Bulgarian)

* New translations en-us.ts (Czech)

* New translations en-us.ts (German)

* New translations en-us.ts (Greek)

* New translations en-us.ts (Finnish)

* New translations en-us.ts (Hungarian)

* New translations en-us.ts (Italian)

* New translations en-us.ts (Korean)

* New translations en-us.ts (Polish)

* New translations en-us.ts (Russian)

* New translations en-us.ts (Slovenian)

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (Swedish)

* New translations en-us.ts (Turkish)

* New translations en-us.ts (Ukrainian)

* New translations en-us.ts (Chinese Simplified)

* New translations en-us.ts (Chinese Traditional)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Thai)

* New translations en-us.ts (Dutch, Belgium)

* New translations en-us.ts (Arabic, Egypt)

* New translations en-us.ts (Italian)

* New translations en-us.ts (German)

* New translations en-us.ts (French)

* New translations en-us.ts (Danish)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Vietnamese)

* New translations en-us.ts (Spanish)

* New translations en-us.ts (Bulgarian)

* New translations en-us.ts (Czech)

* New translations en-us.ts (German)

* New translations en-us.ts (Greek)

* New translations en-us.ts (Finnish)

* New translations en-us.ts (Hungarian)

* New translations en-us.ts (Italian)

* New translations en-us.ts (Korean)

* New translations en-us.ts (Polish)

* New translations en-us.ts (Russian)

* New translations en-us.ts (Slovenian)

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (Swedish)

* New translations en-us.ts (Turkish)

* New translations en-us.ts (Ukrainian)

* New translations en-us.ts (Chinese Simplified)

* New translations en-us.ts (Chinese Traditional)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Thai)

* New translations en-us.ts (Dutch, Belgium)

* New translations en-us.ts (Arabic, Egypt)

* New translations en-us.ts (Italian)
2024-10-23 14:04:41 +02:00
Alexander Lehmann
0b355b94c4 chore(translations): change his to their for describing user (#655) 2024-10-21 08:50:37 +02:00
edbourque0
6444a9d553 docs(installation): Add Portainer installation and upgrade steps (#651)
* Change general config icon

* Update package.json

* Add portainer installation documentation

---------

Co-authored-by: Elias Schneider <login@eliasschneider.com>
2024-10-18 20:56:14 +02:00
Elias Schneider
08079744a0 release: 1.2.2 2024-10-18 16:09:19 +02:00
Elias Schneider
558dd2fb15 chore(translations): add missing translations 2024-10-18 16:07:56 +02:00
Elias Schneider
fe085b58a5 fix: environment variable API_URL can't be changed 2024-10-18 16:07:32 +02:00
edbourque0
958b79d787 fix(admin): change general config icon to gear (#649)
* Change general config icon

* Update package.json

---------

Co-authored-by: Elias Schneider <login@eliasschneider.com>
2024-10-17 21:07:15 +02:00
Papyfloflo
ede9c2a816 refactor: updated wording and grammar in en-US.ts (#648)
Changes made: Revised wording, grammar, and punctuation in various sections such as navbar, home, account, admin, and error messages.
2024-10-17 20:43:14 +02:00
Elias Schneider
e195565630 release: 1.2.1 2024-10-15 20:58:36 +02:00
Elias Schneider
520f9abcf7 chore(translations): update translations via Crowdin (#637)
* New translations en-us.ts (Polish)

* New translations en-us.ts (Italian)

* New translations en-us.ts (French)

* New translations en-us.ts (German)

* New translations en-us.ts (French)

* New translations en-us.ts (Danish)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Vietnamese)

* New translations en-us.ts (Spanish)

* New translations en-us.ts (Bulgarian)

* New translations en-us.ts (Czech)

* New translations en-us.ts (German)

* New translations en-us.ts (Greek)

* New translations en-us.ts (Finnish)

* New translations en-us.ts (Hungarian)

* New translations en-us.ts (Italian)

* New translations en-us.ts (Korean)

* New translations en-us.ts (Polish)

* New translations en-us.ts (Russian)

* New translations en-us.ts (Slovenian)

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (Swedish)

* New translations en-us.ts (Turkish)

* New translations en-us.ts (Ukrainian)

* New translations en-us.ts (Chinese Simplified)

* New translations en-us.ts (Chinese Traditional)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Thai)

* New translations en-us.ts (Dutch, Belgium)

* New translations en-us.ts (Arabic, Egypt)
2024-10-15 20:57:24 +02:00
Marvin A. Ruder
bfbe8de98a fix(oauth): add post_logout_redirect_uri to OAuth logout redirect URI (#638)
* Add `post_logout_redirect_uri` to OAuth logout redirect URI

Signed-off-by: Marvin A. Ruder <signed@mruder.dev>

* Update OAuth2 configuration documentation

Signed-off-by: Marvin A. Ruder <signed@mruder.dev>

---------

Signed-off-by: Marvin A. Ruder <signed@mruder.dev>
2024-10-15 20:49:43 +02:00
Elias Schneider
d5cd3002a1 fix: share can't be created if an invalid email is entered in mail recipients 2024-10-15 20:47:04 +02:00
Elias Schneider
77a092a3cf fix: trim username, email and password on sign in and sign up page 2024-10-15 20:29:34 +02:00
Elias Schneider
613bae9033 fix: error message for invalid max use count of reverse share 2024-10-15 20:28:05 +02:00
Elias Schneider
2e692241c5 fix: disallow passwort reset if it's a ldap user 2024-10-15 20:12:56 +02:00
Elias Schneider
1e96011793 refactor: run formatter 2024-10-15 20:12:09 +02:00
Elias Schneider
522a041ca1 release: 1.2.0 2024-10-14 18:19:02 +02:00
Elias Schneider
ce6430da9f chore(translations): update translations via Crowdin (#636)
* New translations en-us.ts (French)

* New translations en-us.ts (Danish)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Vietnamese)

* New translations en-us.ts (Spanish)

* New translations en-us.ts (Bulgarian)

* New translations en-us.ts (Czech)

* New translations en-us.ts (Greek)

* New translations en-us.ts (Finnish)

* New translations en-us.ts (Hungarian)

* New translations en-us.ts (Italian)

* New translations en-us.ts (Korean)

* New translations en-us.ts (Polish)

* New translations en-us.ts (Russian)

* New translations en-us.ts (Slovenian)

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (Swedish)

* New translations en-us.ts (Turkish)

* New translations en-us.ts (Ukrainian)

* New translations en-us.ts (Chinese Simplified)

* New translations en-us.ts (Chinese Traditional)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Thai)

* New translations en-us.ts (Dutch, Belgium)

* New translations en-us.ts (Arabic, Egypt)
2024-10-14 18:18:47 +02:00
Marvin A. Ruder
2b3ce3ffd2 feat(oauth): Add option to logout from OpenID Connect provider
* Fixes #598

Signed-off-by: Marvin A. Ruder <signed@mruder.dev>
2024-10-14 18:16:47 +02:00
Elias Schneider
104cc06145 chore(translations): update translations via Crowdin (#622)
* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Italian)

* New translations en-us.ts (Vietnamese)

* New translations en-us.ts (Danish)

* New translations en-us.ts (French)

* New translations en-us.ts (Danish)

* New translations en-us.ts (Japanese)
2024-10-14 17:16:53 +02:00
Elias Schneider
4a50a5aa3b Merge branch 'main' of https://github.com/stonith404/pingvin-share 2024-10-14 17:15:42 +02:00
Elias Schneider
d6b8b56247 fix: use unique port env variable for backend 2024-10-14 17:15:38 +02:00
COMPLEX
5883dff4cf feat(oauth): add ability to limit user IDs for Discord authentication (#621) 2024-09-30 08:53:58 +02:00
Elias Schneider
511ae933fa release: 1.1.3 2024-09-27 16:10:48 +02:00
Elias Schneider
df2521b192 chore(translations): update translations via Crowdin (#602)
* New translations en-us.ts (Dutch, Belgium)

* New translations en-us.ts (Dutch, Belgium)

* New translations en-us.ts (German)

* New translations en-us.ts (French)

* New translations en-us.ts (French)

* New translations en-us.ts (German)

* New translations en-us.ts (Italian)

* New translations en-us.ts (French)

* New translations en-us.ts (Greek)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Czech)

* New translations en-us.ts (Bulgarian)

* New translations en-us.ts (Polish)

* New translations en-us.ts (Spanish)

* New translations en-us.ts (Danish)

* New translations en-us.ts (German)

* New translations en-us.ts (Finnish)

* New translations en-us.ts (Hungarian)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Korean)

* New translations en-us.ts (Russian)

* New translations en-us.ts (Slovenian)

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (Swedish)

* New translations en-us.ts (Turkish)

* New translations en-us.ts (Ukrainian)

* New translations en-us.ts (Chinese Simplified)

* New translations en-us.ts (Chinese Traditional)

* New translations en-us.ts (Vietnamese)

* New translations en-us.ts (Thai)

* New translations en-us.ts (Dutch, Belgium)

* New translations en-us.ts (Arabic, Egypt)
2024-09-27 16:10:18 +02:00
Elias Schneider
8f16d6b53e refactor: run formatter 2024-09-27 16:03:53 +02:00
WolverinDEV
3310fe53b3 feat: improve the LDAP implementation (#615)
* feat(logging): add PV_LOG_LEVEL environment variable to set backend log level

* feat(ldap): Adding a more verbose logging output to debug LDAP issues

* fix(ldap): fixed user logins with special characters within the users dn by switching to ldapts

* feat(ldap): made the member of and email attribute names configurable

* fix(ldap): properly handle email like usernames and fixing #601

* Revert "fix: disable email login if ldap is enabled"

This reverts commit d9cfe697d6.

* feat(ldap): disable the ability for a user to change his email when it's a LDAP user

* feat(ldap): relaxed username pattern by allowing the @ character in usernames
2024-09-27 16:02:49 +02:00
Elias Schneider
adc4af996d fix: omit invalid username characters in oidc registration 2024-09-26 21:50:23 +02:00
Elias Schneider
61edc4f4f6 docs: add demo link to docs sidebar 2024-09-25 15:37:05 +02:00
Elias Schneider
eba7984a0f release: 1.1.2 2024-09-24 12:21:56 +02:00
Elias Schneider
69752b8b41 fix: enable secure cookies if app url starts with https 2024-09-24 12:21:41 +02:00
Elias Schneider
ee73293c0f fix: disable auto complete for email recipients and share password 2024-09-24 10:24:48 +02:00
Elias Schneider
5553607ffe Merge branch 'main' of https://github.com/stonith404/pingvin-share 2024-09-22 22:08:39 +02:00
Elias Schneider
2ca6e6ee5f docs: change stand-alone installation command 2024-09-22 22:08:35 +02:00
Alexander Lehmann
18135b0ec0 Remove env line and add comment about update-env (#606) 2024-09-22 18:39:08 +02:00
Sven Kortekaas
f8bfb8ec3c chore(translations): Update nl-BE.ts
New translations and typos
2024-09-19 15:19:50 +02:00
Elias Schneider
187911e334 chore(translations): update translations via Crowdin (#596)
* New translations en-us.ts (Italian)

* New translations en-us.ts (French)

* New translations en-us.ts (Greek)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Czech)

* New translations en-us.ts (Bulgarian)

* New translations en-us.ts (Polish)

* New translations en-us.ts (Spanish)

* New translations en-us.ts (Danish)

* New translations en-us.ts (German)

* New translations en-us.ts (Finnish)

* New translations en-us.ts (Hungarian)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Korean)

* New translations en-us.ts (Russian)

* New translations en-us.ts (Slovenian)

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (Swedish)

* New translations en-us.ts (Turkish)

* New translations en-us.ts (Ukrainian)

* New translations en-us.ts (Chinese Simplified)

* New translations en-us.ts (Chinese Traditional)

* New translations en-us.ts (Vietnamese)

* New translations en-us.ts (Thai)

* New translations en-us.ts (Dutch, Belgium)

* New translations en-us.ts (Arabic, Egypt)

* New translations en-us.ts (Italian)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Italian)

* New translations en-us.ts (French)

* New translations en-us.ts (Greek)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Czech)

* New translations en-us.ts (Bulgarian)

* New translations en-us.ts (Polish)

* New translations en-us.ts (Spanish)

* New translations en-us.ts (Danish)

* New translations en-us.ts (German)

* New translations en-us.ts (Finnish)

* New translations en-us.ts (Hungarian)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Korean)

* New translations en-us.ts (Russian)

* New translations en-us.ts (Slovenian)

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (Swedish)

* New translations en-us.ts (Turkish)

* New translations en-us.ts (Ukrainian)

* New translations en-us.ts (Chinese Simplified)

* New translations en-us.ts (Chinese Traditional)

* New translations en-us.ts (Vietnamese)

* New translations en-us.ts (Thai)

* New translations en-us.ts (Dutch, Belgium)

* New translations en-us.ts (Arabic, Egypt)

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Italian)
2024-09-19 15:15:27 +02:00
Elias Schneider
64acae11a2 docs: update clamav docs 2024-09-19 08:35:28 +02:00
Elias Schneider
6b39adfd03 release: 1.1.1 2024-09-18 23:32:52 +02:00
Elias Schneider
d9cfe697d6 fix: disable email login if ldap is enabled 2024-09-18 23:32:09 +02:00
Elias Schneider
67a0fc6ea5 docs: improve ClamAV docs 2024-09-18 23:16:41 +02:00
Elias Schneider
b13a81a88c feat: add environment variable to trust the reverse proxy 2024-09-18 23:01:50 +02:00
Elias Schneider
97dc3ecfdd chore(docs): dump dependencies 2024-09-18 11:08:05 +02:00
Elias Schneider
d00d52baa9 chore: dump dependencies 2024-09-18 11:04:06 +02:00
Elias Schneider
4c8848a2d9 release: 1.1.0 2024-09-14 18:15:44 +02:00
Elias Schneider
3c8500008d chore: fix wrong versioning for minor releases 2024-09-14 18:15:36 +02:00
Elias Schneider
325122b802 refactor: run formatter 2024-09-14 18:13:32 +02:00
Elias Schneider
7dc2e56fee feat: auto redirect to oauth provider 2024-09-14 18:13:18 +02:00
Elias Schneider
8b3e28bac8 feat: allow smpt without username and password 2024-09-14 17:24:19 +02:00
Elias Schneider
347026b6d3 chore(translations): update translations via Crowdin (#589)
* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Polish)

* New translations en-us.ts (Polish)
2024-09-12 20:32:36 +02:00
Elias Schneider
5a204d38a4 docs: add contribute and sponsers section to README 2024-09-12 20:32:02 +02:00
Elias Schneider
2eeb858f36 docs: improve Pocket ID text in README 2024-09-06 16:37:29 +02:00
Elias Schneider
67faa860da Merge branch 'main' of https://github.com/stonith404/pingvin-share 2024-09-06 16:36:00 +02:00
Elias Schneider
beca26871d docs: add Pocket ID as a tip to the README 2024-09-06 16:35:56 +02:00
Helly
15d1756a4e Add basic configuration to the docs (#587)
* Created website for the docs inside /docs

* remove old docs and home page

* fix wrong redirection path

* remove most of the docs from the readme

* fix docs path

* undo package.json changes

* remove unused images

* rename "how to" route

* Add basic configuration to the docs

---------

Co-authored-by: Elias Schneider <login@eliasschneider.com>
2024-09-06 09:16:50 +02:00
Elias Schneider
be202d3d41 release: 1.0.4 2024-09-06 09:03:11 +02:00
Elias Schneider
f0e785b1a2 New translations en-us.ts (Greek) (#585) 2024-09-06 09:03:02 +02:00
Elias Schneider
92e1e82e09 fix: oauth2 login can fail in some cases because the user can't be found 2024-09-06 09:02:30 +02:00
Elias Schneider
0670aaa331 release: 1.0.3 2024-09-03 22:56:19 +02:00
Elias Schneider
10b71e7035 chore(translations): update translations via Crowdin (#580)
* New translations en-us.ts (Bulgarian)

* New translations en-us.ts (Bulgarian)

* New translations en-us.ts (Czech)

* New translations en-us.ts (German)

* New translations en-us.ts (Czech)

* New translations en-us.ts (Bulgarian)

* New translations en-us.ts (Italian)

* New translations en-us.ts (French)

* New translations en-us.ts (Czech)
2024-09-03 22:56:05 +02:00
Elias Schneider
dee70987eb fix: improve oidc error logging 2024-09-03 22:55:44 +02:00
Elias Schneider
3d2b978daf refactor: run formatter 2024-09-03 22:54:53 +02:00
Elias Schneider
e813da05ae chore(translations): add Bulgarian language files 2024-08-30 08:33:26 +02:00
Elias Schneider
1fba0fd546 chore(translations): update translations via Crowdin (#571)
* New translations en-us.ts (Vietnamese)

* New translations en-us.ts (Vietnamese)
2024-08-30 08:32:29 +02:00
Elias Schneider
96cd353669 release: 1.0.2 2024-08-28 11:11:10 +02:00
Elias Schneider
3e0735c620 fix: default logo not displayed on fresh installations 2024-08-28 11:10:53 +02:00
Elias Schneider
d05988f281 chore(translations): update translations via Crowdin (#569)
* New translations en-us.ts (Vietnamese)

* New translations en-us.ts (Vietnamese)
2024-08-28 09:00:14 +02:00
Elias Schneider
42a985be04 chore: change logs input to textarea 2024-08-27 22:18:47 +02:00
Elias Schneider
af472af3bb chore: add logs to issue template 2024-08-27 22:18:22 +02:00
Elias Schneider
f53f71f054 chore(translations): add Viatnamese translation files 2024-08-27 22:13:06 +02:00
Elias Schneider
5622f9eb2f chore(translations): update translations via Crowdin (#562)
* New translations en-us.ts (Czech)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (German)

* New translations en-us.ts (Italian)

* New translations en-us.ts (French)

* New translations en-us.ts (Spanish)

* New translations en-us.ts (Danish)

* New translations en-us.ts (Greek)

* New translations en-us.ts (Finnish)

* New translations en-us.ts (Hungarian)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Korean)

* New translations en-us.ts (Polish)

* New translations en-us.ts (Russian)

* New translations en-us.ts (Slovenian)

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (Swedish)

* New translations en-us.ts (Turkish)

* New translations en-us.ts (Ukrainian)

* New translations en-us.ts (Chinese Simplified)

* New translations en-us.ts (Chinese Traditional)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Thai)

* New translations en-us.ts (Dutch, Belgium)

* New translations en-us.ts (Arabic, Egypt)

* New translations en-us.ts (Czech)

* New translations en-us.ts (German)

* New translations en-us.ts (Italian)

* New translations en-us.ts (French)

* New translations en-us.ts (Spanish)

* New translations en-us.ts (Danish)

* New translations en-us.ts (Greek)

* New translations en-us.ts (Finnish)

* New translations en-us.ts (Hungarian)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Korean)

* New translations en-us.ts (Polish)

* New translations en-us.ts (Russian)

* New translations en-us.ts (Slovenian)

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (Swedish)

* New translations en-us.ts (Turkish)

* New translations en-us.ts (Ukrainian)

* New translations en-us.ts (Chinese Simplified)

* New translations en-us.ts (Chinese Traditional)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Thai)

* New translations en-us.ts (Dutch, Belgium)

* New translations en-us.ts (Arabic, Egypt)

* New translations en-us.ts (Czech)

* New translations en-us.ts (Czech)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Czech)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Czech)

* New translations en-us.ts (Chinese Simplified)

* New translations en-us.ts (Chinese Simplified)
2024-08-27 22:11:41 +02:00
Helly
02b9abf6c5 docs: fix mistake in README.md (#566) 2024-08-25 22:57:06 +02:00
Elias Schneider
6a4c3bf58f release: 1.0.1 2024-08-25 22:15:51 +02:00
Elias Schneider
64efac5b68 fix(translations): add missing string for ldap group 2024-08-25 22:03:23 +02:00
Timothy
8c5c696c51 feat(email): add {email} placeholder to user invitation email (#564)
* feat(email): add {email} placeholder to user invitation email

* change default values and setting description

---------

Co-authored-by: Elias Schneider <login@eliasschneider.com>
2024-08-25 21:58:40 +02:00
Elias Schneider
01da83cdf6 docs: use user prefered color scheme 2024-08-25 17:47:08 +02:00
Elias Schneider
cfcc5cebac docs: update security.md 2024-08-25 16:06:55 +02:00
Elias Schneider
b96878b6b1 release: 1.0.0 2024-08-25 16:02:46 +02:00
Elias Schneider
9c381a2ed6 fix: internal server error if user has no password when trying to sign in 2024-08-25 16:00:49 +02:00
Elias Schneider
4f9b4f38f6 docs: fix docusaurus edit link 2024-08-25 15:42:21 +02:00
Elias Schneider
c98b237259 chore(translations): add Czech files 2024-08-25 15:33:35 +02:00
Elias Schneider
17d593a794 chore(translations): update translations via Crowdin (#545)
* New translations en-us.ts (French)

* New translations en-us.ts (Spanish)

* New translations en-us.ts (Danish)

* New translations en-us.ts (German)

* New translations en-us.ts (Greek)

* New translations en-us.ts (Finnish)

* New translations en-us.ts (Hungarian)

* New translations en-us.ts (Italian)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Korean)

* New translations en-us.ts (Polish)

* New translations en-us.ts (Russian)

* New translations en-us.ts (Slovenian)

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (Swedish)

* New translations en-us.ts (Ukrainian)

* New translations en-us.ts (Chinese Simplified)

* New translations en-us.ts (Chinese Traditional)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Thai)

* New translations en-us.ts (Dutch, Belgium)

* New translations en-us.ts (Arabic, Egypt)

* New translations en-us.ts (Turkish)

* New translations en-us.ts (Italian)

* New translations en-us.ts (French)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Italian)

* New translations en-us.ts (Italian)

* New translations en-us.ts (Spanish)

* New translations en-us.ts (Spanish)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (German)

* New translations en-us.ts (Italian)

* New translations en-us.ts (German)

* New translations en-us.ts (Italian)

* New translations en-us.ts (German)

* New translations en-us.ts (Italian)

* New translations en-us.ts (French)

* New translations en-us.ts (Spanish)

* New translations en-us.ts (Danish)

* New translations en-us.ts (Greek)

* New translations en-us.ts (Finnish)

* New translations en-us.ts (Hungarian)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Korean)

* New translations en-us.ts (Polish)

* New translations en-us.ts (Russian)

* New translations en-us.ts (Slovenian)

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (Swedish)

* New translations en-us.ts (Turkish)

* New translations en-us.ts (Ukrainian)

* New translations en-us.ts (Chinese Simplified)

* New translations en-us.ts (Chinese Traditional)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Thai)

* New translations en-us.ts (Dutch, Belgium)

* New translations en-us.ts (Arabic, Egypt)

* New translations en-us.ts (Hungarian)
2024-08-25 15:30:46 +02:00
Helly
ac580b79b4 docs: add docusaurus docs
* Created website for the docs inside /docs

* remove old docs and home page

* fix wrong redirection path

* remove most of the docs from the readme

* fix docs path

* undo package.json changes

* remove unused images

* rename "how to" route

---------

Co-authored-by: Elias Schneider <login@eliasschneider.com>
2024-08-24 17:30:11 +02:00
WolverinDEV
4186a768b3 feat(ldap): Adding support for LDAP authentication (#554) 2024-08-24 16:15:33 +02:00
Matt Burns
4924f76394 fix: get started button on home page not working when sign-up is disabled
* Redirect to sign in page if sign ups are disabled on an instance

* Add a comment back, add a comment in

* Remove english default Get Started text
2024-08-20 22:53:46 +02:00
Elias Schneider
f1f514dff7 chore: move docker entrypoint to seperate script 2024-08-11 15:53:56 +02:00
Elias Schneider
94e2a6110d docs: add npx prisma generate to update instructions 2024-08-11 14:58:16 +02:00
Elias Schneider
7716f5c0ce chore: add sponsor to README 2024-08-03 00:31:46 +02:00
Elias Schneider
0a963bfaf1 release: 0.29.0 2024-07-30 08:43:30 +02:00
Elias Schneider
472c93d548 chore: save caddy logs to caddy.log 2024-07-30 08:43:11 +02:00
Elias Schneider
93aacca9b4 refactor: run formatter 2024-07-30 08:39:22 +02:00
Elias Schneider
3505669135 chore(translations): update translations via Crowdin (#540)
* New translations en-us.ts (French)

* New translations en-us.ts (Hungarian)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Japanese)
2024-07-30 08:27:36 +02:00
Ivan Li
fe735f9704 feat: add more options to reverse shares (#495)
* feat(reverse-share): optional simplified interface for reverse sharing. issue #155.

* chore: Remove useless form validation.

* feat: Share Ready modal adds a prompt that an email has been sent to the reverse share creator.

* fix: Simplified reverse shared interface elements lack spacing when not logged in.

* fix: Share Ready modal prompt contrast is too low in dark mode.

* feat: add public access options to reverse share.

* feat: remember reverse share simplified and publicAccess options in cookies.

* style: npm run format.

* chore(i18n): Improve translation.

Co-authored-by: Elias Schneider <login@eliasschneider.com>

Update frontend/src/i18n/translations/en-US.ts

Co-authored-by: Elias Schneider <login@eliasschneider.com>

Update frontend/src/i18n/translations/en-US.ts

Co-authored-by: Elias Schneider <login@eliasschneider.com>

chore(i18n): Improve translation.

* chore: Improved variable naming.

* chore(i18n): Improve translation. x2.

* fix(backend/shares): Misjudged the permission of the share of the reverse share.
2024-07-30 08:26:56 +02:00
Elias Schneider
3563715f57 chore(frontend): remove unused dependency 2024-07-28 16:09:31 +02:00
Elias Schneider
14c2185e6f Revert "fix: set max age of access token cookie to 15 minutes"
This reverts commit 2dac38560b.
2024-07-27 17:15:20 +02:00
Elias Schneider
27ee9fb6cb feat: sort share files by name by default 2024-07-25 19:32:00 +02:00
Elias Schneider
601772d2f4 release: 0.28.0 2024-07-22 13:36:54 +02:00
Elias Schneider
0e66be5f08 chore: resolve uncomplete merge conflict 2024-07-22 13:36:41 +02:00
Elias Schneider
4cabcfb715 chore(translations): update translations via Crowdin (#532)
* New translations en-us.ts (French)

* New translations en-us.ts (Spanish)

* New translations en-us.ts (Danish)

* New translations en-us.ts (German)

* New translations en-us.ts (Greek)

* New translations en-us.ts (Finnish)

* New translations en-us.ts (Hungarian)

* New translations en-us.ts (Italian)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Korean)

* New translations en-us.ts (Polish)

* New translations en-us.ts (Russian)

* New translations en-us.ts (Slovenian)

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (Swedish)

* New translations en-us.ts (Ukrainian)

* New translations en-us.ts (Chinese Simplified)

* New translations en-us.ts (Chinese Traditional)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Thai)

* New translations en-us.ts (Dutch, Belgium)

* New translations en-us.ts (Arabic, Egypt)

* New translations en-us.ts (Turkish)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Italian)

* New translations en-us.ts (French)

* New translations en-us.ts (Spanish)

* New translations en-us.ts (Danish)

* New translations en-us.ts (German)

* New translations en-us.ts (Greek)

* New translations en-us.ts (Finnish)

* New translations en-us.ts (Hungarian)

* New translations en-us.ts (Italian)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Korean)

* New translations en-us.ts (Polish)

* New translations en-us.ts (Russian)

* New translations en-us.ts (Slovenian)

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (Swedish)

* New translations en-us.ts (Ukrainian)

* New translations en-us.ts (Chinese Simplified)

* New translations en-us.ts (Chinese Traditional)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Thai)

* New translations en-us.ts (Dutch, Belgium)

* New translations en-us.ts (Arabic, Egypt)

* New translations en-us.ts (Turkish)

* New translations en-us.ts (Italian)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Arabic, Egypt)
2024-07-22 11:01:20 +02:00
Maurice Schorn
e5e9d85d39 chore: remove obsolete version from docker compose
* compose version tag is not a necessity

* adjust default nextjs port

* Update docker-compose.yml

---------

Co-authored-by: Elias Schneider <login@eliasschneider.com>
2024-07-17 23:26:57 +02:00
Marvin A. Ruder
70fd2d94be feat(auth): Add role-based access management from OpenID Connect (#535)
* feat(auth): Add role-based access management from OpenID Connect

Signed-off-by: Marvin A. Ruder <signed@mruder.dev>

* Apply suggestions from code review

Signed-off-by: Marvin A. Ruder <signed@mruder.dev>

---------

Signed-off-by: Marvin A. Ruder <signed@mruder.dev>
2024-07-17 23:25:42 +02:00
Elias Schneider
e5a0c649e3 fix: store only 10 share tokens in the cookies and clear the expired ones 2024-07-16 19:17:53 +02:00
Anti-Apple4life
414bcecbb5 chore: fix compile-time errors and warnings in i18n translations (#531)
* Fix aingle-quote warning in fi-FI.ts

* Fix duplicate key in fr-FR.ts
2024-07-11 23:43:15 +02:00
Elias Schneider
968352cb6c release: 0.27.0 2024-07-11 21:57:37 +02:00
Elias Schneider
355f860387 chore(translations): update translations via Crowdin (#524)
* New translations en-us.ts (French)

* New translations en-us.ts (Spanish)

* New translations en-us.ts (Danish)

* New translations en-us.ts (German)

* New translations en-us.ts (Greek)

* New translations en-us.ts (Finnish)

* New translations en-us.ts (Hungarian)

* New translations en-us.ts (Italian)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Korean)

* New translations en-us.ts (Polish)

* New translations en-us.ts (Russian)

* New translations en-us.ts (Slovenian)

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (Swedish)

* New translations en-us.ts (Ukrainian)

* New translations en-us.ts (Chinese Simplified)

* New translations en-us.ts (Chinese Traditional)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Thai)

* New translations en-us.ts (Dutch, Belgium)

* New translations en-us.ts (Arabic, Egypt)

* New translations en-us.ts (Turkish)

* New translations en-us.ts (Italian)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (French)

* New translations en-us.ts (French)

* New translations en-us.ts (Spanish)

* New translations en-us.ts (Danish)

* New translations en-us.ts (German)

* New translations en-us.ts (Greek)

* New translations en-us.ts (Finnish)

* New translations en-us.ts (Hungarian)

* New translations en-us.ts (Italian)

* New translations en-us.ts (Japanese)

* New translations en-us.ts (Korean)

* New translations en-us.ts (Polish)

* New translations en-us.ts (Russian)

* New translations en-us.ts (Slovenian)

* New translations en-us.ts (Serbian (Cyrillic))

* New translations en-us.ts (Swedish)

* New translations en-us.ts (Ukrainian)

* New translations en-us.ts (Chinese Simplified)

* New translations en-us.ts (Chinese Traditional)

* New translations en-us.ts (Portuguese, Brazilian)

* New translations en-us.ts (Thai)

* New translations en-us.ts (Dutch, Belgium)

* New translations en-us.ts (Arabic, Egypt)

* New translations en-us.ts (Turkish)
2024-07-11 21:57:08 +02:00
thecrafterjt
083d82c28b feat(smtp): allow unauthorized mail server certificates (#525)
* Update config.seed.ts

Added Config Option "allowUnauthenticatedCertificates".

* Update email.service.ts

Now using new Config Option "allowUnauthenticatedCertificates".

* Update en-US.ts

* Update ar-EG.ts

* Update da-DK.ts

* Update el-GR.ts

* Update es-ES.ts

* Update fi-FI.ts

* Update fr-FR.ts

* Update hu-HU.ts

* Update it-IT.ts

* Update ja-JP.ts

* Update ko-KR.ts

* Update nl-BE.ts

* Update pl-PL.ts

* Update pt-BR.ts

* Update ru-RU.ts

* Update sl-SI.ts

* Update sr-SP.ts

* Update sv-SE.ts

* Update th-TH.ts

* Update tr-TR.ts

* Update uk-UA.ts

* Update zh-CN.ts

* Update zh-TW.ts

* Update config.seed.ts

* Update email.service.ts

* Update de-DE.ts

* Add files via upload

rename allow-unauthenticated-certificates to allow-unauthorized-certificates

* Add files via upload

rename allowUnauthenticatedCertificates to allowUnauthorizedCertificates

* Add files via upload

rename allowUnauthenticatedCertificates to allowUnauthorizedCertificates

* rename "unauthenticated" to "unauthorized"

* refactor: run formatter

---------

Co-authored-by: Elias Schneider <login@eliasschneider.com>
2024-07-11 21:50:09 +02:00
Elias Schneider
046c630abf Merge branches 'main' and 'main' of https://github.com/stonith404/pingvin-share 2024-07-10 18:39:53 +02:00
Elias Schneider
d2bfb9a55f feat: add logs for successful registration, successful login and failed login 2024-07-10 18:39:47 +02:00
Elias Schneider
fccf57e9e4 chore(translations): update translations via Crowdin (#520)
* New translations en-us.ts (Hungarian)

* New translations en-us.ts (Korean)
2024-07-07 23:08:28 +02:00
Marvin A. Ruder
e1a68f75f7 feat(auth): Allow to hide username / password login form when OAuth is enabled (#518)
* 🚀 Feature: Allow to hide username / password login form when OAuth is enabled

* Hide “Sign in” password form
* Disable routes related to password authentication
* Change styling of OAuth provider buttons
* Open OAuth page in same tab
* Fix consistent usage of informal language in de-DE locale

Fixes #489

Signed-off-by: Marvin A. Ruder <signed@mruder.dev>

* fix: order of new config variables

---------

Signed-off-by: Marvin A. Ruder <signed@mruder.dev>
Co-authored-by: Elias Schneider <login@eliasschneider.com>
2024-07-07 23:08:14 +02:00
138 changed files with 24339 additions and 4958 deletions

View File

@@ -31,14 +31,13 @@ body:
label: "👎 Actual Behavior"
description: "What did actually happen? Add screenshots, if applicable."
placeholder: "It actually ..."
- type: input
- type: textarea
id: operating-system
attributes:
label: "🌐 Browser"
description: "Which browser do you use?"
placeholder: "Firefox"
label: "📜 Logs"
description: "Paste any relevant logs here."
validations:
required: true
required: false
- type: markdown
attributes:
value: |

6
.gitignore vendored
View File

@@ -7,6 +7,9 @@ node_modules
/frontend/.next/
/frontend/out/
# yarn
yarn.lock
# build
build/
dist/
@@ -38,6 +41,9 @@ yarn-error.log*
# project specific
/backend/data/
/data/
/docs/build/
/docs/.docusaurus
/docs/.cache-loader
# Jetbrains specific (webstorm)
.idea/**/**

View File

@@ -1,3 +1,229 @@
## [1.6.1](https://github.com/stonith404/pingvin-share/compare/v1.6.0...v1.6.1) (2024-11-26)
### Bug Fixes
* error for non oidc oauth clients ([ba2e7e1](https://github.com/stonith404/pingvin-share/commit/ba2e7e122c45bfb2a783b15438112a79fee0c307))
## [1.6.0](https://github.com/stonith404/pingvin-share/compare/v1.5.0...v1.6.0) (2024-11-25)
### Features
* add config variable to specify the requested OIDC sopes ([da54ce6](https://github.com/stonith404/pingvin-share/commit/da54ce6ee020a9718f55ec30c614607d411f55c8))
### Bug Fixes
* add validation for share id and zip compression config variables ([3160f90](https://github.com/stonith404/pingvin-share/commit/3160f90e1d4bb3d6aa4017e98e400929fc4d3b2e))
## [1.5.0](https://github.com/stonith404/pingvin-share/compare/v1.4.0...v1.5.0) (2024-11-24)
### Features
* **share:** add share ID length setting ([#677](https://github.com/stonith404/pingvin-share/issues/677)) ([9d4bb55](https://github.com/stonith404/pingvin-share/commit/9d4bb55a0945450f8a42c212d7f23983db38f37f))
### Bug Fixes
* totp can't be enabled if user is a ldap user ([c8f05f2](https://github.com/stonith404/pingvin-share/commit/c8f05f2475a5a54550cf64ef57c8b612580273be))
## [1.4.0](https://github.com/stonith404/pingvin-share/compare/v1.3.0...v1.4.0) (2024-11-17)
### Features
* add "creatorEmail" config bariable to share recipient email message ([c7dacb2](https://github.com/stonith404/pingvin-share/commit/c7dacb26e87504a1c5e6b0d87cdcd5ed91b9cdf5))
### Bug Fixes
* remote arbitrary file overwrite on file upload endpoint ([6cf5c66](https://github.com/stonith404/pingvin-share/commit/6cf5c66fe2eda1e0a525edf7440d047fe2f0e35b))
## [1.3.0](https://github.com/stonith404/pingvin-share/compare/v1.2.4...v1.3.0) (2024-11-14)
### Features
* add 'secureCookies' configuration variable to explicitly set the secure flag and prevent confusion ([4ce6420](https://github.com/stonith404/pingvin-share/commit/4ce64206be7440a99299e1ed238ced7408c0563d))
* add confirm dialog for leaving the page if an upload is in progress ([d8084e4](https://github.com/stonith404/pingvin-share/commit/d8084e401d7572b2d6e38ffa20cb678a0fb0e615))
### Bug Fixes
* improve share completed dialog redirection for reverse shares ([4ef7ebb](https://github.com/stonith404/pingvin-share/commit/4ef7ebb0622f16d2d2c4d114b5fc15298e2ba24f))
* prevent deletion of last admin account ([e1a5d19](https://github.com/stonith404/pingvin-share/commit/e1a5d195448e3d741b77fb982ce515489a360562))
* throw error if no disk space is left ([c26de4e](https://github.com/stonith404/pingvin-share/commit/c26de4e881edfe6c7db617c0aeba89871397ebe2))
* use current window url instead of app url in frontend ([6f45c3b](https://github.com/stonith404/pingvin-share/commit/6f45c3b1fbf4a95b29e5742878b55a1afa0b8886))
## [1.2.4](https://github.com/stonith404/pingvin-share/compare/v1.2.3...v1.2.4) (2024-10-24)
### Bug Fixes
* don't enforce password lenght for sign in form because of LDAP ([428c1d2](https://github.com/stonith404/pingvin-share/commit/428c1d2b993a05a25cc94aabe56216b9ab969fa1))
* use app name as totp issuer ([c89ca7e](https://github.com/stonith404/pingvin-share/commit/c89ca7e64b08f437dd1b7e9bf2b9d674cc612228))
## [1.2.3](https://github.com/stonith404/pingvin-share/compare/v1.2.2...v1.2.3) (2024-10-23)
### Bug Fixes
* share password can be bypassed if a deleted share with the same id was visited before ([acbff6e](https://github.com/stonith404/pingvin-share/commit/acbff6e129d236452180f8b96775457d135ac080))
## [1.2.2](https://github.com/stonith404/pingvin-share/compare/v1.2.1...v1.2.2) (2024-10-18)
### Bug Fixes
* **admin:** change general config icon to gear ([#649](https://github.com/stonith404/pingvin-share/issues/649)) ([958b79d](https://github.com/stonith404/pingvin-share/commit/958b79d787585c367a693872fd105a326e6e8d38))
* environment variable `API_URL` can't be changed ([fe085b5](https://github.com/stonith404/pingvin-share/commit/fe085b58a5f3c0152df12957aa150c0876c2a074))
## [1.2.1](https://github.com/stonith404/pingvin-share/compare/v1.2.0...v1.2.1) (2024-10-15)
### Bug Fixes
* disallow passwort reset if it's a ldap user ([2e69224](https://github.com/stonith404/pingvin-share/commit/2e692241c57b001c9312302523c6374c0c24ea0c))
* error message for invalid max use count of reverse share ([613bae9](https://github.com/stonith404/pingvin-share/commit/613bae90330a76c0964352a3fe927df3697309eb))
* **oauth:** add `post_logout_redirect_uri` to OAuth logout redirect URI ([#638](https://github.com/stonith404/pingvin-share/issues/638)) ([bfbe8de](https://github.com/stonith404/pingvin-share/commit/bfbe8de98a6a7a2d32dd8d4dddbcc1d4ce6388f4))
* share can't be created if an invalid email is entered in mail recipients ([d5cd300](https://github.com/stonith404/pingvin-share/commit/d5cd3002a1661e58d584e12280be36f17948c38c))
* trim username, email and password on sign in and sign up page ([77a092a](https://github.com/stonith404/pingvin-share/commit/77a092a3cf089a4aa8b9897b5ad14e5500181d10))
## [1.2.0](https://github.com/stonith404/pingvin-share/compare/v1.1.3...v1.2.0) (2024-10-14)
### Features
* **oauth:** add ability to limit user IDs for Discord authentication ([#621](https://github.com/stonith404/pingvin-share/issues/621)) ([5883dff](https://github.com/stonith404/pingvin-share/commit/5883dff4cf0abe99b3ac8f0b56fdc9d04e80b51c))
* **oauth:** Add option to logout from OpenID Connect provider ([2b3ce3f](https://github.com/stonith404/pingvin-share/commit/2b3ce3ffd250f7e3052d43c1c1e76947abf91e55)), closes [#598](https://github.com/stonith404/pingvin-share/issues/598)
### Bug Fixes
* use unique port env variable for backend ([d6b8b56](https://github.com/stonith404/pingvin-share/commit/d6b8b56247814087c2b676fe2367300172b5a94b))
## [1.1.3](https://github.com/stonith404/pingvin-share/compare/v1.1.2...v1.1.3) (2024-09-27)
### Features
* improve the LDAP implementation ([#615](https://github.com/stonith404/pingvin-share/issues/615)) ([3310fe5](https://github.com/stonith404/pingvin-share/commit/3310fe53b3e4c89db78d57ede6c8d57d8137ecc1)), closes [#601](https://github.com/stonith404/pingvin-share/issues/601)
### Bug Fixes
* omit invalid username characters in oidc registration ([adc4af9](https://github.com/stonith404/pingvin-share/commit/adc4af996d30b295b06e4ee517aa53be62c0f6c1))
## [1.1.2](https://github.com/stonith404/pingvin-share/compare/v1.1.1...v1.1.2) (2024-09-24)
### Bug Fixes
* disable auto complete for email recipients and share password ([ee73293](https://github.com/stonith404/pingvin-share/commit/ee73293c0f822d3e79cfefd096c656d4c36a12d1))
* enable secure cookies if app url starts with https ([69752b8](https://github.com/stonith404/pingvin-share/commit/69752b8b417edda1ab4a4acedbdda09d545d6df8))
## [1.1.1](https://github.com/stonith404/pingvin-share/compare/v1.1.0...v1.1.1) (2024-09-18)
### Features
* add environment variable to trust the reverse proxy ([b13a81a](https://github.com/stonith404/pingvin-share/commit/b13a81a88ca871c5714b2ed52d0e12fb7ceca176))
### Bug Fixes
* disable email login if ldap is enabled ([d9cfe69](https://github.com/stonith404/pingvin-share/commit/d9cfe697d66e9db7bfbc2252b3700580793ce9bb))
## [1.1.0](https://github.com/stonith404/pingvin-share/compare/v1.0.4...v1.1.0) (2024-09-14)
### Features
* allow smpt without username and password ([8b3e28b](https://github.com/stonith404/pingvin-share/commit/8b3e28bac83e5326234096445395046ebdb0c4d7))
* auto redirect to oauth provider ([7dc2e56](https://github.com/stonith404/pingvin-share/commit/7dc2e56fee1afc1078774cc702c0f1fee9bae938))
## [1.0.4](https://github.com/stonith404/pingvin-share/compare/v1.0.3...v1.0.4) (2024-09-06)
### Bug Fixes
* oauth2 login can fail in some cases because the user can't be found ([92e1e82](https://github.com/stonith404/pingvin-share/commit/92e1e82e095075edf04019887f9c2048c21d00d6))
## [1.0.3](https://github.com/stonith404/pingvin-share/compare/v1.0.2...v1.0.3) (2024-09-03)
### Bug Fixes
* improve oidc error logging ([dee7098](https://github.com/stonith404/pingvin-share/commit/dee70987eb74eda4a9ab7332522fa5540cee9761))
## [1.0.2](https://github.com/stonith404/pingvin-share/compare/v1.0.1...v1.0.2) (2024-08-28)
### Bug Fixes
* default logo not displayed on fresh installations ([3e0735c](https://github.com/stonith404/pingvin-share/commit/3e0735c62079ac777fd08051b7e7602eebf74a5d))
## [1.0.1](https://github.com/stonith404/pingvin-share/compare/v1.0.0...v1.0.1) (2024-08-25)
### Features
* **email:** add {email} placeholder to user invitation email ([#564](https://github.com/stonith404/pingvin-share/issues/564)) ([8c5c696](https://github.com/stonith404/pingvin-share/commit/8c5c696c514a5fb450462184240b21553d7f1532))
### Bug Fixes
* **translations:** add missing string for ldap group ([64efac5](https://github.com/stonith404/pingvin-share/commit/64efac5b685bf2de9d65c6a4f8890d45afe6476d))
## [1.0.0](https://github.com/stonith404/pingvin-share/compare/v0.29.0...v1.0.0) (2024-08-25)
### Features
* **ldap:** Adding support for LDAP authentication ([#554](https://github.com/stonith404/pingvin-share/issues/554)) ([4186a76](https://github.com/stonith404/pingvin-share/commit/4186a768b310855282bc4876d1f294700963b8f5))
### Bug Fixes
* get started button on home page not working when sign-up is disabled ([4924f76](https://github.com/stonith404/pingvin-share/commit/4924f763947c9a6b79ba0d85887f104ed9545c78))
* internal server error if user has no password when trying to sign in ([9c381a2](https://github.com/stonith404/pingvin-share/commit/9c381a2ed6b3b7dfd95d4278889b937ffb85e01b))
## [0.29.0](https://github.com/stonith404/pingvin-share/compare/v0.28.0...v0.29.0) (2024-07-30)
### Features
* add more options to reverse shares ([#495](https://github.com/stonith404/pingvin-share/issues/495)) ([fe735f9](https://github.com/stonith404/pingvin-share/commit/fe735f9704c9d96398f3127a559e17848b08d140)), closes [#155](https://github.com/stonith404/pingvin-share/issues/155)
* sort share files by name by default ([27ee9fb](https://github.com/stonith404/pingvin-share/commit/27ee9fb6cb98177661bed20a0baa399b27e70b7e))
### Reverts
* Revert "fix: set max age of access token cookie to 15 minutes" ([14c2185](https://github.com/stonith404/pingvin-share/commit/14c2185e6f1a81d63e25fbeec3e30a54cf6a44c5))
## [0.28.0](https://github.com/stonith404/pingvin-share/compare/v0.27.0...v0.28.0) (2024-07-22)
### Features
* **auth:** Add role-based access management from OpenID Connect ([#535](https://github.com/stonith404/pingvin-share/issues/535)) ([70fd2d9](https://github.com/stonith404/pingvin-share/commit/70fd2d94be3411cc430f5c56e522028398127efb))
### Bug Fixes
* store only 10 share tokens in the cookies and clear the expired ones ([e5a0c64](https://github.com/stonith404/pingvin-share/commit/e5a0c649e36e0db419d04446affe2564c45cf321))
## [0.27.0](https://github.com/stonith404/pingvin-share/compare/v0.26.0...v0.27.0) (2024-07-11)
### Features
* add logs for successful registration, successful login and failed login ([d2bfb9a](https://github.com/stonith404/pingvin-share/commit/d2bfb9a55fdad6a05377b8552471cf1151304c90))
* **auth:** Allow to hide username / password login form when OAuth is enabled ([#518](https://github.com/stonith404/pingvin-share/issues/518)) ([e1a68f7](https://github.com/stonith404/pingvin-share/commit/e1a68f75f7b034f1ef9e45f26de584f13e355589)), closes [#489](https://github.com/stonith404/pingvin-share/issues/489)
* **smtp:** allow unauthorized mail server certificates ([#525](https://github.com/stonith404/pingvin-share/issues/525)) ([083d82c](https://github.com/stonith404/pingvin-share/commit/083d82c28b835c178f076e89ef8f5885e8ea31cb))
## [0.26.0](https://github.com/stonith404/pingvin-share/compare/v0.25.0...v0.26.0) (2024-07-03)

View File

@@ -1,15 +0,0 @@
:3000 {
# Reverse proxy for /api
reverse_proxy /api/* http://localhost:8080 {
header_up X-Forwarded-Host {host}:{server_port}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
# Reverse proxy for all other requests
reverse_proxy http://localhost:3333 {
header_up X-Forwarded-Host {host}:{server_port}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}

View File

@@ -30,13 +30,10 @@ RUN npm run build && npm prune --production
FROM node:20-alpine AS runner
ENV NODE_ENV=docker
# Install Caddy
RUN apk update --no-cache \
&& apk upgrade --no-cache \
&& apk add --no-cache curl caddy
COPY ./Caddyfile /etc/caddy/Caddyfile
WORKDIR /opt/app/frontend
COPY --from=frontend-builder /opt/app/public ./public
COPY --from=frontend-builder /opt/app/.next/standalone ./
@@ -49,12 +46,13 @@ COPY --from=backend-builder /opt/app/dist ./dist
COPY --from=backend-builder /opt/app/prisma ./prisma
COPY --from=backend-builder /opt/app/package.json ./
COPY ./reverse-proxy /etc/caddy
COPY ./scripts/docker-entrypoint.sh /opt/app/docker-entrypoint.sh
WORKDIR /opt/app
EXPOSE 3000
# Health check remains unchanged
HEALTHCHECK --interval=10s --timeout=3s CMD curl -f http://localhost:3000/api/health || exit 1
# Application startup updated for Caddy
CMD cp -rn /tmp/img/* /opt/app/frontend/public/img && caddy run --config /etc/caddy/Caddyfile & PORT=3333 HOSTNAME=0.0.0.0 node frontend/server.js & cd backend && npm run prod
CMD ["sh", "/opt/app/docker-entrypoint.sh"]

140
README.md
View File

@@ -2,11 +2,9 @@
[![](https://dcbadge.limes.pink/api/server/wHRQ9nFRcK)](https://discord.gg/wHRQ9nFRcK) [![](https://img.shields.io/badge/Crowdin-2E3340.svg?style=for-the-badge&logo=Crowdin&logoColor=white)](https://crowdin.com/project/pingvin-share) [![](https://img.shields.io/badge/sponsor-30363D?style=for-the-badge&logo=GitHub-Sponsors&logoColor=#white)](https://github.com/sponsors/stonith404)
_Read this in another language: [Spanish](/docs/README.es.md), [English](/README.md), [简体中文](/docs/README.zh-cn.md), [日本語](/docs/README.ja-jp.md)_
---
Pingvin Share is self-hosted file sharing platform and an alternative for WeTransfer.
Pingvin Share is a self-hosted file sharing platform and an alternative for WeTransfer.
## ✨ Features
@@ -15,6 +13,8 @@ Pingvin Share is self-hosted file sharing platform and an alternative for WeTran
- Set an expiration date for shares
- Secure shares with visitor limits and passwords
- Email recipients
- Reverse shares
- OIDC and LDAP authentication
- Integration with ClamAV for security scans
## 🐧 Get to know Pingvin Share
@@ -26,8 +26,6 @@ Pingvin Share is self-hosted file sharing platform and an alternative for WeTran
## ⌨️ Setup
> Note: Pingvin Share is in its early stages and may contain bugs.
### Installation with Docker (recommended)
1. Download the `docker-compose.yml` file
@@ -35,135 +33,19 @@ Pingvin Share is self-hosted file sharing platform and an alternative for WeTran
The website is now listening on `http://localhost:3000`, have fun with Pingvin Share 🐧!
### Stand-alone Installation
> [!TIP]
> Checkout [Pocket ID](https://github.com/stonith404/pocket-id), a user-friendly OIDC provider that lets you easily log in to services like Pingvin Share using Passkeys.
Required tools:
## 📚 Documentation
- [Node.js](https://nodejs.org/en/download/) >= 16
- [Git](https://git-scm.com/downloads)
- [pm2](https://pm2.keymetrics.io/) for running Pingvin Share in the background
```bash
git clone https://github.com/stonith404/pingvin-share
cd pingvin-share
# Checkout the latest version
git fetch --tags && git checkout $(git describe --tags `git rev-list --tags --max-count=1`)
# Start the backend
cd backend
npm install
npm run build
pm2 start --name="pingvin-share-backend" npm -- run prod
# Start the frontend
cd ../frontend
npm install
npm run build
API_URL=http://localhost:8080 # Set the URL of the backend, default: http://localhost:8080
pm2 start --name="pingvin-share-frontend" .next/standalone/server.js
```
**Uploading Large Files**: By default, Pingvin Share uses a built-in reverse proxy to reduce the installation steps. However, this reverse proxy is not optimized for uploading large files. If you wish to upload larger files, you can either use the Docker installation or set up your own reverse proxy. An example configuration for Caddy can be found in `./Caddyfile`.
The website is now listening on `http://localhost:3000`, have fun with Pingvin Share 🐧!
### Integrations
#### ClamAV (Docker only)
ClamAV is used to scan shares for malicious files and remove them if found.
1. Add the ClamAV container to the Docker Compose stack (see `docker-compose.yml`) and start the container.
2. Docker will wait for ClamAV to start before starting Pingvin Share. This may take a minute or two.
3. The Pingvin Share logs should now log "ClamAV is active"
Please note that ClamAV needs a lot of [ressources](https://docs.clamav.net/manual/Installing/Docker.html#memory-ram-requirements).
#### OAuth 2 Login
View the [OAuth 2 guide](/docs/oauth2-guide.md) for more information.
### Additional resources
- [Synology NAS installation](https://mariushosting.com/how-to-install-pingvin-share-on-your-synology-nas/)
- [Zeabur installation](https://zeabur.com/templates/19G6OK)
### Upgrade to a new version
As Pingvin Share is in early stage, see the release notes for breaking changes before upgrading.
#### Docker
```bash
docker compose pull
docker compose up -d
```
#### Stand-alone
1. Stop the running app
```bash
pm2 stop pingvin-share-backend pingvin-share-frontend
```
2. Repeat the steps from the [installation guide](#stand-alone-installation) except the `git clone` step.
```bash
cd pingvin-share
# Checkout the latest version
git fetch --tags && git checkout $(git describe --tags `git rev-list --tags --max-count=1`)
# Start the backend
cd backend
npm install
npm run build
pm2 restart pingvin-share-backend
# Start the frontend
cd ../frontend
npm install
npm run build
API_URL=http://localhost:8080 # Set the URL of the backend, default: http://localhost:8080
pm2 restart pingvin-share-frontend
```
### Configuration
You can customize Pingvin Share like changing your domain by going to the configuration page in your admin dashboard `/admin/config`.
#### Environment variables
For installation specific configuration, you can use environment variables. The following variables are available:
##### Backend
| Variable | Default Value | Description |
| ---------------- | -------------------------------------------------- | -------------------------------------- |
| `PORT` | `8080` | The port on which the backend listens. |
| `DATABASE_URL` | `file:../data/pingvin-share.db?connection_limit=1` | The URL of the SQLite database. |
| `DATA_DIRECTORY` | `./data` | The directory where data is stored. |
| `CLAMAV_HOST` | `127.0.0.1` | The IP address of the ClamAV server. |
| `CLAMAV_PORT` | `3310` | The port number of the ClamAV server. |
##### Frontend
| Variable | Default Value | Description |
| --------- | ----------------------- | ---------------------------------------- |
| `PORT` | `3000` | The port on which the frontend listens. |
| `API_URL` | `http://localhost:8080` | The URL of the backend for the frontend. |
For more installation options and advanced configurations, please refer to the [documentation](https://stonith404.github.io/pingvin-share).
## 🖤 Contribute
### Translations
We would love it if you want to help make Pingvin Share better! You can either [help to translate](https://stonith404.github.io/pingvin-share/help-out/translate) Pingvin Share or [contribute to the codebase](https://stonith404.github.io/pingvin-share/help-out/contribute).
You can help to translate Pingvin Share into your language.
On [Crowdin](https://crowdin.com/project/pingvin-share) you can easily translate Pingvin Share online.
## ❤️ Sponsors
Is your language not on Crowdin? Feel free to [Request it](https://github.com/stonith404/pingvin-share/issues/new?assignees=&labels=language-request&projects=&template=language-request.yml&title=%F0%9F%8C%90+Language+request%3A+%3Clanguage+name+in+english%3E).
Thank you for supporting Pingvin Share 🙏
Any issues while translating? Feel free to participate in the [Localization discussion](https://github.com/stonith404/pingvin-share/discussions/198).
### Project
You're very welcome to contribute to Pingvin Share! Please follow the [contribution guide](/CONTRIBUTING.md) to get started.
- [@COMPLEXWASTAKEN](https://github.com/COMPLEXWASTAKEN)

View File

@@ -1,7 +1,9 @@
# Security Policy
## Supported Versions
As Pingvin Share is in beta, older versions don't get security updates. Please consider to update Pingvin Share regularly. Updates can be automated with e.g [Watchtower](https://github.com/containrrr/watchtower).
Older versions of Pingvin Share do not receive security updates. To ensure your system remains secure, we strongly recommend updating Pingvin Share regularly. You can automate these updates using tools like [Watchtower](https://github.com/containrrr/watchtower).
## Reporting a Vulnerability
Thank you for taking the time to report a vulnerability. Please DO NOT create an issue on GitHub because the vulnerability could get exploited. Instead please write an email to [elias@eliasschneider.com](mailto:elias@eliasschneider.com).

2202
backend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "pingvin-share-backend",
"version": "0.26.0",
"version": "1.6.1",
"scripts": {
"build": "nest build",
"dev": "cross-env NODE_ENV=development nest start --watch",
@@ -14,70 +14,74 @@
},
"dependencies": {
"@nestjs/cache-manager": "^2.2.2",
"@nestjs/common": "^10.3.9",
"@nestjs/config": "^3.2.2",
"@nestjs/core": "^10.3.9",
"@nestjs/common": "^10.4.3",
"@nestjs/config": "^3.2.3",
"@nestjs/core": "^10.4.3",
"@nestjs/jwt": "^10.2.0",
"@nestjs/passport": "^10.0.3",
"@nestjs/platform-express": "^10.3.9",
"@nestjs/schedule": "^4.0.2",
"@nestjs/swagger": "^7.3.1",
"@nestjs/throttler": "^5.2.0",
"@prisma/client": "^5.16.1",
"@nestjs/platform-express": "^10.4.3",
"@nestjs/schedule": "^4.1.1",
"@nestjs/swagger": "^7.4.2",
"@nestjs/throttler": "^6.2.1",
"@prisma/client": "^5.19.1",
"@types/jmespath": "^0.15.2",
"archiver": "^7.0.1",
"argon2": "^0.40.3",
"body-parser": "^1.20.2",
"cache-manager": "^5.6.1",
"clamscan": "^2.2.1",
"argon2": "^0.41.1",
"body-parser": "^1.20.3",
"cache-manager": "^5.7.6",
"clamscan": "^2.3.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"content-disposition": "^0.5.4",
"cookie-parser": "^1.4.6",
"jmespath": "^0.16.0",
"ldapts": "^7.2.0",
"mime-types": "^2.1.35",
"moment": "^2.30.1",
"nanoid": "^3.3.7",
"nodemailer": "^6.9.14",
"nodemailer": "^6.9.15",
"otplib": "^12.0.1",
"passport": "^0.7.0",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
"qrcode-svg": "^1.1.0",
"reflect-metadata": "^0.2.2",
"rimraf": "^5.0.7",
"rimraf": "^6.0.1",
"rxjs": "^7.8.1",
"sharp": "^0.33.4",
"sharp": "^0.33.5",
"ts-node": "^10.9.2"
},
"devDependencies": {
"@nestjs/cli": "^10.3.2",
"@nestjs/schematics": "^10.1.1",
"@nestjs/testing": "^10.3.9",
"@nestjs/cli": "^10.4.5",
"@nestjs/schematics": "^10.1.4",
"@nestjs/testing": "^10.4.3",
"@types/archiver": "^6.0.2",
"@types/clamscan": "^2.0.8",
"@types/cookie-parser": "^1.4.7",
"@types/cron": "^2.0.1",
"@types/cron": "^2.4.0",
"@types/express": "^4.17.21",
"@types/mime-types": "^2.1.4",
"@types/multer": "^1.4.11",
"@types/node": "^20.14.9",
"@types/nodemailer": "^6.4.15",
"@types/multer": "^1.4.12",
"@types/node": "^22.5.5",
"@types/nodemailer": "^6.4.16",
"@types/passport-jwt": "^4.0.1",
"@types/qrcode-svg": "^1.1.4",
"@types/sharp": "^0.31.1",
"@types/qrcode-svg": "^1.1.5",
"@types/sharp": "^0.32.0",
"@types/supertest": "^6.0.2",
"@typescript-eslint/eslint-plugin": "^7.14.1",
"@typescript-eslint/parser": "^7.14.1",
"@types/uuid": "^10.0.0",
"@typescript-eslint/eslint-plugin": "^8.6.0",
"@typescript-eslint/parser": "^8.6.0",
"cross-env": "^7.0.3",
"eslint": "^8.56.0",
"eslint": "^9.10.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
"newman": "^6.1.3",
"prettier": "^3.3.2",
"prisma": "^5.16.1",
"eslint-plugin-prettier": "^5.2.1",
"newman": "^6.2.1",
"prettier": "^3.3.3",
"prisma": "^5.19.1",
"source-map-support": "^0.5.21",
"ts-loader": "^9.5.1",
"tsconfig-paths": "4.2.0",
"typescript": "^5.5.2",
"wait-on": "^7.2.0"
"typescript": "^5.6.2",
"wait-on": "^8.0.1"
}
}

View File

@@ -0,0 +1,20 @@
-- RedefineTables
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_ReverseShare" (
"id" TEXT NOT NULL PRIMARY KEY,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"token" TEXT NOT NULL,
"shareExpiration" DATETIME NOT NULL,
"maxShareSize" TEXT NOT NULL,
"sendEmailNotification" BOOLEAN NOT NULL,
"remainingUses" INTEGER NOT NULL,
"simplified" BOOLEAN NOT NULL DEFAULT false,
"creatorId" TEXT NOT NULL,
CONSTRAINT "ReverseShare_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
INSERT INTO "new_ReverseShare" ("createdAt", "creatorId", "id", "maxShareSize", "remainingUses", "sendEmailNotification", "shareExpiration", "token") SELECT "createdAt", "creatorId", "id", "maxShareSize", "remainingUses", "sendEmailNotification", "shareExpiration", "token" FROM "ReverseShare";
DROP TABLE "ReverseShare";
ALTER TABLE "new_ReverseShare" RENAME TO "ReverseShare";
CREATE UNIQUE INDEX "ReverseShare_token_key" ON "ReverseShare"("token");
PRAGMA foreign_key_check;
PRAGMA foreign_keys=ON;

View File

@@ -0,0 +1,22 @@
-- RedefineTables
PRAGMA defer_foreign_keys=ON;
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_ReverseShare" (
"id" TEXT NOT NULL PRIMARY KEY,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"token" TEXT NOT NULL,
"shareExpiration" DATETIME NOT NULL,
"maxShareSize" TEXT NOT NULL,
"sendEmailNotification" BOOLEAN NOT NULL,
"remainingUses" INTEGER NOT NULL,
"simplified" BOOLEAN NOT NULL DEFAULT false,
"publicAccess" BOOLEAN NOT NULL DEFAULT true,
"creatorId" TEXT NOT NULL,
CONSTRAINT "ReverseShare_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
INSERT INTO "new_ReverseShare" ("createdAt", "creatorId", "id", "maxShareSize", "remainingUses", "sendEmailNotification", "shareExpiration", "simplified", "token") SELECT "createdAt", "creatorId", "id", "maxShareSize", "remainingUses", "sendEmailNotification", "shareExpiration", "simplified", "token" FROM "ReverseShare";
DROP TABLE "ReverseShare";
ALTER TABLE "new_ReverseShare" RENAME TO "ReverseShare";
CREATE UNIQUE INDEX "ReverseShare_token_key" ON "ReverseShare"("token");
PRAGMA foreign_keys=ON;
PRAGMA defer_foreign_keys=OFF;

View File

@@ -0,0 +1,11 @@
/*
Warnings:
- A unique constraint covering the columns `[ldapDN]` on the table `User` will be added. If there are existing duplicate values, this will fail.
*/
-- AlterTable
ALTER TABLE "User" ADD COLUMN "ldapDN" TEXT;
-- CreateIndex
CREATE UNIQUE INDEX "User_ldapDN_key" ON "User"("ldapDN");

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "RefreshToken" ADD COLUMN "oauthIDToken" TEXT;

View File

@@ -16,6 +16,7 @@ model User {
email String @unique
password String?
isAdmin Boolean @default(false)
ldapDN String? @unique
shares Share[]
refreshTokens RefreshToken[]
@@ -39,6 +40,8 @@ model RefreshToken {
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
oauthIDToken String? // prefixed with the ID of the issuing OAuth provider, separated by a colon
}
model LoginToken {
@@ -103,6 +106,8 @@ model ReverseShare {
maxShareSize String
sendEmailNotification Boolean
remainingUses Int
simplified Boolean @default(false)
publicAccess Boolean @default(true)
creatorId String
creator User @relation(fields: [creatorId], references: [id], onDelete: Cascade)

View File

@@ -20,6 +20,10 @@ const configVariables: ConfigVariables = {
defaultValue: "http://localhost:3000",
secret: false,
},
secureCookies: {
type: "boolean",
defaultValue: "false",
},
showHomePage: {
type: "boolean",
defaultValue: "true",
@@ -47,6 +51,11 @@ const configVariables: ConfigVariables = {
defaultValue: "0",
secret: false,
},
shareIdLength: {
type: "number",
defaultValue: "8",
secret: false,
},
maxSize: {
type: "number",
defaultValue: "1000000000",
@@ -71,7 +80,6 @@ const configVariables: ConfigVariables = {
enableShareEmailRecipients: {
type: "boolean",
defaultValue: "false",
secret: false,
},
shareRecipientsSubject: {
@@ -81,7 +89,7 @@ const configVariables: ConfigVariables = {
shareRecipientsMessage: {
type: "text",
defaultValue:
"Hey!\n\n{creator} shared some files with you, view or download the files with this link: {shareUrl}\n\nThe share will expire {expires}.\n\nNote: {desc}\n\nShared securely with Pingvin Share 🐧",
"Hey!\n\n{creator} ({creatorEmail}) shared some files with you, view or download the files with this link: {shareUrl}\n\nThe share will expire {expires}.\n\nNote: {desc}\n\nShared securely with Pingvin Share 🐧",
},
reverseShareSubject: {
type: "string",
@@ -108,7 +116,7 @@ const configVariables: ConfigVariables = {
inviteMessage: {
type: "text",
defaultValue:
"Hey!\n\nYou were invited to Pingvin Share. Click this link to accept the invite: {url}\n\nYour password is: {password}\n\nPingvin Share 🐧",
'Hey!\n\nYou were invited to Pingvin Share. Click this link to accept the invite: {url}\n\nYou can use the email "{email}" and the password "{password}" to sign in.\n\nPingvin Share 🐧',
},
},
smtp: {
@@ -117,6 +125,12 @@ const configVariables: ConfigVariables = {
defaultValue: "false",
secret: false,
},
allowUnauthorizedCertificates: {
type: "boolean",
defaultValue: "false",
secret: false,
},
host: {
type: "string",
defaultValue: "",
@@ -139,6 +153,51 @@ const configVariables: ConfigVariables = {
obscured: true,
},
},
ldap: {
enabled: {
type: "boolean",
defaultValue: "false",
secret: false,
},
url: {
type: "string",
defaultValue: "",
},
bindDn: {
type: "string",
defaultValue: "",
},
bindPassword: {
type: "string",
defaultValue: "",
obscured: true,
},
searchBase: {
type: "string",
defaultValue: "",
},
searchQuery: {
type: "string",
defaultValue: ""
},
adminGroups: {
type: "string",
defaultValue: ""
},
fieldNameMemberOf: {
type: "string",
defaultValue: "memberOf",
},
fieldNameEmail: {
type: "string",
defaultValue: "userPrincipalName",
}
},
oauth: {
"allowRegistration": {
type: "boolean",
@@ -148,6 +207,11 @@ const configVariables: ConfigVariables = {
type: "boolean",
defaultValue: "true",
},
"disablePassword": {
type: "boolean",
defaultValue: "false",
secret: false,
},
"github-enabled": {
type: "boolean",
defaultValue: "false",
@@ -199,6 +263,10 @@ const configVariables: ConfigVariables = {
type: "string",
defaultValue: "",
},
"discord-limitedUsers": {
type: "string",
defaultValue: "",
},
"discord-clientId": {
type: "string",
defaultValue: "",
@@ -216,10 +284,30 @@ const configVariables: ConfigVariables = {
type: "string",
defaultValue: "",
},
"oidc-signOut": {
type: "boolean",
defaultValue: "false",
},
"oidc-scope": {
type: "string",
defaultValue: "openid email profile",
},
"oidc-usernameClaim": {
type: "string",
defaultValue: "",
},
"oidc-rolePath": {
type: "string",
defaultValue: "",
},
"oidc-roleGeneralAccess": {
type: "string",
defaultValue: "",
},
"oidc-roleAdminAccess": {
type: "string",
defaultValue: "",
},
"oidc-clientId": {
type: "string",
defaultValue: "",
@@ -229,7 +317,7 @@ const configVariables: ConfigVariables = {
defaultValue: "",
obscured: true,
},
}
},
};
type ConfigVariables = {
@@ -281,12 +369,15 @@ async function seedConfigVariables() {
async function migrateConfigVariables() {
const existingConfigVariables = await prisma.config.findMany();
const orderMap: { [category: string]: number } = {};
for (const existingConfigVariable of existingConfigVariables) {
const configVariable =
configVariables[existingConfigVariable.category]?.[
existingConfigVariable.name
];
// Delete the config variable if it doesn't exist in the seed
if (!configVariable) {
await prisma.config.delete({
where: {
@@ -297,15 +388,11 @@ async function migrateConfigVariables() {
},
});
// Update the config variable if the metadata changed
} else if (
JSON.stringify({
...configVariable,
name: existingConfigVariable.name,
category: existingConfigVariable.category,
value: existingConfigVariable.value,
}) != JSON.stringify(existingConfigVariable)
) {
// Update the config variable if it exists in the seed
} else {
const variableOrder = Object.keys(
configVariables[existingConfigVariable.category]
).indexOf(existingConfigVariable.name);
await prisma.config.update({
where: {
name_category: {
@@ -318,8 +405,10 @@ async function migrateConfigVariables() {
name: existingConfigVariable.name,
category: existingConfigVariable.category,
value: existingConfigVariable.value,
order: variableOrder,
},
});
orderMap[existingConfigVariable.category] = variableOrder + 1;
}
}
}

View File

@@ -45,12 +45,13 @@ export class AuthController {
})
async signUp(
@Body() dto: AuthRegisterDTO,
@Req() { ip }: Request,
@Res({ passthrough: true }) response: Response,
) {
if (!this.config.get("share.allowRegistration"))
throw new ForbiddenException("Registration is not allowed");
const result = await this.authService.signUp(dto);
const result = await this.authService.signUp(dto, ip);
this.authService.addTokensToResponse(
response,
@@ -71,9 +72,10 @@ export class AuthController {
@HttpCode(200)
async signIn(
@Body() dto: AuthSignInDTO,
@Req() { ip }: Request,
@Res({ passthrough: true }) response: Response,
) {
const result = await this.authService.signIn(dto);
const result = await this.authService.signIn(dto, ip);
if (result.accessToken && result.refreshToken) {
this.authService.addTokensToResponse(
@@ -118,7 +120,7 @@ export class AuthController {
})
@HttpCode(202)
async requestResetPassword(@Param("email") email: string) {
this.authService.requestResetPassword(email);
await this.authService.requestResetPassword(email);
}
@Post("resetPassword")
@@ -170,13 +172,25 @@ export class AuthController {
@Req() request: Request,
@Res({ passthrough: true }) response: Response,
) {
await this.authService.signOut(request.cookies.access_token);
response.cookie("access_token", "accessToken", { maxAge: -1 });
const redirectURI = await this.authService.signOut(
request.cookies.access_token,
);
const isSecure = this.config.get("general.secureCookies");
response.cookie("access_token", "", {
maxAge: -1,
secure: isSecure,
});
response.cookie("refresh_token", "", {
path: "/api/auth/token",
httpOnly: true,
maxAge: -1,
secure: isSecure,
});
if (typeof redirectURI === "string") {
return { redirectURI: redirectURI.toString() };
}
}
@Post("totp/enable")

View File

@@ -1,10 +1,13 @@
import { Module } from "@nestjs/common";
import { forwardRef, Module } from "@nestjs/common";
import { JwtModule } from "@nestjs/jwt";
import { EmailModule } from "src/email/email.module";
import { AuthController } from "./auth.controller";
import { AuthService } from "./auth.service";
import { AuthTotpService } from "./authTotp.service";
import { JwtStrategy } from "./strategy/jwt.strategy";
import { LdapService } from "./ldap.service";
import { UserModule } from "../user/user.module";
import { OAuthModule } from "../oauth/oauth.module";
@Module({
imports: [
@@ -12,9 +15,11 @@ import { JwtStrategy } from "./strategy/jwt.strategy";
global: true,
}),
EmailModule,
forwardRef(() => OAuthModule),
UserModule,
],
controllers: [AuthController],
providers: [AuthService, AuthTotpService, JwtStrategy],
providers: [AuthService, AuthTotpService, JwtStrategy, LdapService],
exports: [AuthService],
})
export class AuthModule {}

View File

@@ -1,7 +1,10 @@
import {
BadRequestException,
ForbiddenException,
forwardRef,
Inject,
Injectable,
Logger,
UnauthorizedException,
} from "@nestjs/common";
import { JwtService } from "@nestjs/jwt";
@@ -13,8 +16,12 @@ import * as moment from "moment";
import { ConfigService } from "src/config/config.service";
import { EmailService } from "src/email/email.service";
import { PrismaService } from "src/prisma/prisma.service";
import { OAuthService } from "../oauth/oauth.service";
import { GenericOidcProvider } from "../oauth/provider/genericOidc.provider";
import { UserSevice } from "../user/user.service";
import { AuthRegisterDTO } from "./dto/authRegister.dto";
import { AuthSignInDTO } from "./dto/authSignIn.dto";
import { LdapService } from "./ldap.service";
@Injectable()
export class AuthService {
@@ -23,9 +30,13 @@ export class AuthService {
private jwtService: JwtService,
private config: ConfigService,
private emailService: EmailService,
private ldapService: LdapService,
private userService: UserSevice,
@Inject(forwardRef(() => OAuthService)) private oAuthService: OAuthService,
) {}
private readonly logger = new Logger(AuthService.name);
async signUp(dto: AuthRegisterDTO) {
async signUp(dto: AuthRegisterDTO, ip: string, isAdmin?: boolean) {
const isFirstUser = (await this.prisma.user.count()) == 0;
const hash = dto.password ? await argon.hash(dto.password) : null;
@@ -35,7 +46,7 @@ export class AuthService {
email: dto.email,
username: dto.username,
password: hash,
isAdmin: isFirstUser,
isAdmin: isAdmin ?? isFirstUser,
},
});
@@ -44,6 +55,7 @@ export class AuthService {
);
const accessToken = await this.createAccessToken(user, refreshTokenId);
this.logger.log(`User ${user.email} signed up from IP ${ip}`);
return { accessToken, refreshToken, user };
} catch (e) {
if (e instanceof PrismaClientKnownRequestError) {
@@ -57,29 +69,58 @@ export class AuthService {
}
}
async signIn(dto: AuthSignInDTO) {
if (!dto.email && !dto.username)
async signIn(dto: AuthSignInDTO, ip: string) {
if (!dto.email && !dto.username) {
throw new BadRequestException("Email or username is required");
}
const user = await this.prisma.user.findFirst({
where: {
OR: [{ email: dto.email }, { username: dto.username }],
},
});
if (!this.config.get("oauth.disablePassword")) {
const user = await this.prisma.user.findFirst({
where: {
OR: [{ email: dto.email }, { username: dto.username }],
},
});
if (!user || !(await argon.verify(user.password, dto.password)))
throw new UnauthorizedException("Wrong email or password");
if (user?.password && (await argon.verify(user.password, dto.password))) {
this.logger.log(
`Successful password login for user ${user.email} from IP ${ip}`,
);
return this.generateToken(user);
}
}
return this.generateToken(user);
if (this.config.get("ldap.enabled")) {
/*
* E-mail-like user credentials are passed as the email property
* instead of the username. Since the username format does not matter
* when searching for users in LDAP, we simply use the username
* in whatever format it is provided.
*/
const ldapUsername = dto.username || dto.email;
this.logger.debug(`Trying LDAP login for user ${ldapUsername}`);
const ldapUser = await this.ldapService.authenticateUser(
ldapUsername,
dto.password,
);
if (ldapUser) {
const user = await this.userService.findOrCreateFromLDAP(dto, ldapUser);
this.logger.log(
`Successful LDAP login for user ${ldapUsername} (${user.id}) from IP ${ip}`,
);
return this.generateToken(user);
}
}
this.logger.log(
`Failed login attempt for user ${dto.email || dto.username} from IP ${ip}`,
);
throw new UnauthorizedException("Wrong email or password");
}
async generateToken(user: User, isOAuth = false) {
async generateToken(user: User, oauth?: { idToken?: string }) {
// TODO: Make all old loginTokens invalid when a new one is created
// Check if the user has TOTP enabled
if (
user.totpVerified &&
!(isOAuth && this.config.get("oauth.ignoreTotp"))
) {
if (user.totpVerified && !(oauth && this.config.get("oauth.ignoreTotp"))) {
const loginToken = await this.createLoginToken(user.id);
return { loginToken };
@@ -87,6 +128,7 @@ export class AuthService {
const { refreshToken, refreshTokenId } = await this.createRefreshToken(
user.id,
oauth?.idToken,
);
const accessToken = await this.createAccessToken(user, refreshTokenId);
@@ -94,6 +136,9 @@ export class AuthService {
}
async requestResetPassword(email: string) {
if (this.config.get("oauth.disablePassword"))
throw new ForbiddenException("Password sign in is disabled");
const user = await this.prisma.user.findFirst({
where: { email },
include: { resetPasswordToken: true },
@@ -101,6 +146,15 @@ export class AuthService {
if (!user) return;
if (user.ldapDN) {
this.logger.log(
`Failed password reset request for user ${email} because it is an LDAP user`,
);
throw new BadRequestException(
"This account can't reset its password here. Please contact your administrator.",
);
}
// Delete old reset password token
if (user.resetPasswordToken) {
await this.prisma.resetPasswordToken.delete({
@@ -115,10 +169,13 @@ export class AuthService {
},
});
await this.emailService.sendResetPasswordEmail(user.email, token);
this.emailService.sendResetPasswordEmail(user.email, token);
}
async resetPassword(token: string, newPassword: string) {
if (this.config.get("oauth.disablePassword"))
throw new ForbiddenException("Password sign in is disabled");
const user = await this.prisma.user.findFirst({
where: { resetPasswordToken: { token } },
});
@@ -179,12 +236,57 @@ export class AuthService {
}) || {};
if (refreshTokenId) {
const oauthIDToken = await this.prisma.refreshToken
.findFirst({
select: { oauthIDToken: true },
where: { id: refreshTokenId },
})
.then((refreshToken) => refreshToken?.oauthIDToken)
.catch((e) => {
// Ignore error if refresh token doesn't exist
if (e.code != "P2025") throw e;
});
await this.prisma.refreshToken
.delete({ where: { id: refreshTokenId } })
.catch((e) => {
// Ignore error if refresh token doesn't exist
if (e.code != "P2025") throw e;
});
if (typeof oauthIDToken === "string") {
const [providerName, idTokenHint] = oauthIDToken.split(":");
const provider = this.oAuthService.availableProviders()[providerName];
let signOutFromProviderSupportedAndActivated = false;
try {
signOutFromProviderSupportedAndActivated = this.config.get(
`oauth.${providerName}-signOut`,
);
} catch (_) {
// Ignore error if the provider is not supported or if the provider sign out is not activated
}
if (
provider instanceof GenericOidcProvider &&
signOutFromProviderSupportedAndActivated
) {
const configuration = await provider.getConfiguration();
if (
configuration.frontchannel_logout_supported &&
URL.canParse(configuration.end_session_endpoint)
) {
const redirectURI = new URL(configuration.end_session_endpoint);
redirectURI.searchParams.append(
"post_logout_redirect_uri",
this.config.get("general.appUrl"),
);
redirectURI.searchParams.append("id_token_hint", idTokenHint);
redirectURI.searchParams.append(
"client_id",
this.config.get(`oauth.${providerName}-clientId`),
);
return redirectURI.toString();
}
}
}
}
}
@@ -203,13 +305,14 @@ export class AuthService {
);
}
async createRefreshToken(userId: string) {
async createRefreshToken(userId: string, idToken?: string) {
const { id, token } = await this.prisma.refreshToken.create({
data: {
userId,
expiresAt: moment()
.add(this.config.get("general.sessionDuration"), "hours")
.toDate(),
oauthIDToken: idToken,
},
});
@@ -231,16 +334,19 @@ export class AuthService {
refreshToken?: string,
accessToken?: string,
) {
const isSecure = this.config.get("general.secureCookies");
if (accessToken)
response.cookie("access_token", accessToken, {
sameSite: "lax",
maxAge: 1000 * 60 * 15, // 15 minutes
secure: isSecure,
maxAge: 1000 * 60 * 60 * 24 * 30 * 3, // 3 months
});
if (refreshToken)
response.cookie("refresh_token", refreshToken, {
path: "/api/auth/token",
httpOnly: true,
sameSite: "strict",
secure: isSecure,
maxAge: 1000 * 60 * 60 * this.config.get("general.sessionDuration"),
});
}
@@ -262,4 +368,12 @@ export class AuthService {
return null;
}
}
async verifyPassword(user: User, password: string) {
if (!user.password && this.config.get("ldap.enabled")) {
return !!this.ldapService.authenticateUser(user.username, password);
}
return argon.verify(user.password, password);
}
}

View File

@@ -5,9 +5,9 @@ import {
UnauthorizedException,
} from "@nestjs/common";
import { User } from "@prisma/client";
import * as argon from "argon2";
import { authenticator, totp } from "otplib";
import * as qrcode from "qrcode-svg";
import { ConfigService } from "src/config/config.service";
import { PrismaService } from "src/prisma/prisma.service";
import { AuthService } from "./auth.service";
import { AuthSignInTotpDTO } from "./dto/authSignInTotp.dto";
@@ -16,6 +16,7 @@ import { AuthSignInTotpDTO } from "./dto/authSignInTotp.dto";
export class AuthTotpService {
constructor(
private prisma: PrismaService,
private configService: ConfigService,
private authService: AuthService,
) {}
@@ -63,7 +64,7 @@ export class AuthTotpService {
}
async enableTotp(user: User, password: string) {
if (!(await argon.verify(user.password, password)))
if (!this.authService.verifyPassword(user, password))
throw new ForbiddenException("Invalid password");
// Check if we have a secret already
@@ -76,13 +77,10 @@ export class AuthTotpService {
throw new BadRequestException("TOTP is already enabled");
}
const issuer = this.configService.get("general.appName");
const secret = authenticator.generateSecret();
const otpURL = totp.keyuri(
user.username || user.email,
"pingvin-share",
secret,
);
const otpURL = totp.keyuri(user.username || user.email, issuer, secret);
await this.prisma.user.update({
where: { id: user.id },
@@ -107,9 +105,8 @@ export class AuthTotpService {
};
}
// TODO: Maybe require a token to verify that the user who started enabling totp is the one who is verifying it?
async verifyTotp(user: User, password: string, code: string) {
if (!(await argon.verify(user.password, password)))
if (!this.authService.verifyPassword(user, password))
throw new ForbiddenException("Invalid password");
const { totpSecret } = await this.prisma.user.findUnique({
@@ -138,7 +135,7 @@ export class AuthTotpService {
}
async disableTotp(user: User, password: string, code: string) {
if (!(await argon.verify(user.password, password)))
if (!this.authService.verifyPassword(user, password))
throw new ForbiddenException("Invalid password");
const { totpSecret } = await this.prisma.user.findUnique({

View File

@@ -1,8 +1,6 @@
import { PickType } from "@nestjs/swagger";
import { IsEmail, IsOptional, IsString } from "class-validator";
import { UserDTO } from "src/user/dto/user.dto";
export class AuthSignInDTO extends PickType(UserDTO, ["password"] as const) {
export class AuthSignInDTO {
@IsEmail()
@IsOptional()
email: string;
@@ -10,4 +8,7 @@ export class AuthSignInDTO extends PickType(UserDTO, ["password"] as const) {
@IsString()
@IsOptional()
username: string;
@IsString()
password: string;
}

View File

@@ -0,0 +1,105 @@
import { Inject, Injectable, Logger } from "@nestjs/common";
import { inspect } from "node:util";
import { ConfigService } from "../config/config.service";
import { Client, Entry, InvalidCredentialsError } from "ldapts";
@Injectable()
export class LdapService {
private readonly logger = new Logger(LdapService.name);
constructor(
@Inject(ConfigService)
private readonly serviceConfig: ConfigService,
) {}
private async createLdapConnection(): Promise<Client> {
const ldapUrl = this.serviceConfig.get("ldap.url");
if (!ldapUrl) {
throw new Error("LDAP server URL is not defined");
}
const ldapClient = new Client({
url: ldapUrl,
timeout: 15_000,
connectTimeout: 15_000,
});
const bindDn = this.serviceConfig.get("ldap.bindDn") || null;
if (bindDn) {
try {
await ldapClient.bind(
bindDn,
this.serviceConfig.get("ldap.bindPassword"),
);
} catch (error) {
this.logger.warn(`Failed to bind to default user: ${error}`);
throw new Error("failed to bind to default user");
}
}
return ldapClient;
}
public async authenticateUser(
username: string,
password: string,
): Promise<Entry | null> {
if (!username.match(/^[a-zA-Z0-9-_.@]+$/)) {
this.logger.verbose(
`Username ${username} does not match username pattern. Authentication failed.`,
);
return null;
}
const searchBase = this.serviceConfig.get("ldap.searchBase");
const searchQuery = this.serviceConfig
.get("ldap.searchQuery")
.replaceAll("%username%", username);
const ldapClient = await this.createLdapConnection();
try {
const { searchEntries } = await ldapClient.search(searchBase, {
filter: searchQuery,
scope: "sub",
attributes: ["*"],
returnAttributeValues: true,
});
if (searchEntries.length > 1) {
/* too many users found */
this.logger.verbose(
`Authentication for username ${username} failed. Too many users found with query ${searchQuery}`,
);
return null;
} else if (searchEntries.length == 0) {
/* user not found */
this.logger.verbose(
`Authentication for username ${username} failed. No user found with query ${searchQuery}`,
);
return null;
}
const targetEntity = searchEntries[0];
this.logger.verbose(
`Trying to authenticate ${username} against LDAP user ${targetEntity.dn}`,
);
try {
await ldapClient.bind(targetEntity.dn, password);
return targetEntity;
} catch (error) {
if (error instanceof InvalidCredentialsError) {
this.logger.verbose(
`Failed to authenticate ${username} against ${targetEntity.dn}. Invalid credentials.`,
);
return null;
}
this.logger.warn(`User bind failure: ${inspect(error)}`);
return null;
}
} catch (error) {
this.logger.warn(`Connect error: ${inspect(error)}`);
return null;
}
}
}

View File

@@ -5,8 +5,8 @@ import {
NotFoundException,
} from "@nestjs/common";
import { Config } from "@prisma/client";
import { PrismaService } from "src/prisma/prisma.service";
import { EventEmitter } from "events";
import { PrismaService } from "src/prisma/prisma.service";
/**
* ConfigService extends EventEmitter to allow listening for config updates,
@@ -100,6 +100,8 @@ export class ConfigService extends EventEmitter {
);
}
this.validateConfigVariable(key, value);
const updatedVariable = await this.prisma.config.update({
where: {
name_category: {
@@ -116,4 +118,24 @@ export class ConfigService extends EventEmitter {
return updatedVariable;
}
validateConfigVariable(key: string, value: string | number | boolean) {
const validations = [
{
key: "share.shareIdLength",
condition: (value: number) => value >= 2 && value <= 50,
message: "Share ID length must be between 2 and 50",
},
{
key: "share.zipCompressionLevel",
condition: (value: number) => value >= 0 && value <= 9,
message: "Zip compression level must be between 0 and 9",
},
];
const validation = validations.find((validation) => validation.key == key);
if (validation && !validation.condition(value as any)) {
throw new BadRequestException(validation.message);
}
}
}

View File

@@ -1,3 +1,5 @@
import { LogLevel } from "@nestjs/common";
export const DATA_DIRECTORY = process.env.DATA_DIRECTORY || "./data";
export const SHARE_DIRECTORY = `${DATA_DIRECTORY}/uploads/shares`;
export const DATABASE_URL =
@@ -7,3 +9,7 @@ export const CLAMAV_HOST =
process.env.CLAMAV_HOST ||
(process.env.NODE_ENV == "docker" ? "clamav" : "127.0.0.1");
export const CLAMAV_PORT = parseInt(process.env.CLAMAV_PORT) || 3310;
export const LOG_LEVEL_AVAILABLE: LogLevel[] = ['verbose', 'debug', 'log', 'warn', 'error', 'fatal'];
export const LOG_LEVEL_DEFAULT: LogLevel = process.env.NODE_ENV === 'development' ? "verbose" : "log";
export const LOG_LEVEL_ENV = `${process.env.PV_LOG_LEVEL || ""}`;

View File

@@ -17,13 +17,19 @@ export class EmailService {
if (!this.config.get("smtp.enabled"))
throw new InternalServerErrorException("SMTP is disabled");
const username = this.config.get("smtp.username");
const password = this.config.get("smtp.password");
return nodemailer.createTransport({
host: this.config.get("smtp.host"),
port: this.config.get("smtp.port"),
secure: this.config.get("smtp.port") == 465,
auth: {
user: this.config.get("smtp.username"),
pass: this.config.get("smtp.password"),
auth:
username || password ? { user: username, pass: password } : undefined,
tls: {
rejectUnauthorized: !this.config.get(
"smtp.allowUnauthorizedCertificates",
),
},
});
}
@@ -63,6 +69,7 @@ export class EmailService {
.get("email.shareRecipientsMessage")
.replaceAll("\\n", "\n")
.replaceAll("{creator}", creator?.username ?? "Someone")
.replaceAll("{creatorEmail}", creator?.email ?? "")
.replaceAll("{shareUrl}", shareUrl)
.replaceAll("{desc}", description ?? "No description")
.replaceAll(
@@ -111,7 +118,8 @@ export class EmailService {
this.config
.get("email.inviteMessage")
.replaceAll("{url}", loginUrl)
.replaceAll("{password}", password),
.replaceAll("{password}", password)
.replaceAll("{email}", recipientEmail),
);
}

View File

@@ -3,21 +3,23 @@ import {
HttpException,
HttpStatus,
Injectable,
InternalServerErrorException,
NotFoundException,
} from "@nestjs/common";
import { JwtService } from "@nestjs/jwt";
import * as crypto from "crypto";
import * as fs from "fs";
import { createReadStream } from "fs";
import * as fs from "fs/promises";
import * as mime from "mime-types";
import { ConfigService } from "src/config/config.service";
import { PrismaService } from "src/prisma/prisma.service";
import { validate as isValidUUID } from "uuid";
import { SHARE_DIRECTORY } from "../constants";
@Injectable()
export class FileService {
constructor(
private prisma: PrismaService,
private jwtService: JwtService,
private config: ConfigService,
) {}
@@ -27,7 +29,11 @@ export class FileService {
file: { id?: string; name: string },
shareId: string,
) {
if (!file.id) file.id = crypto.randomUUID();
if (!file.id) {
file.id = crypto.randomUUID();
} else if (!isValidUUID(file.id)) {
throw new BadRequestException("Invalid file ID format");
}
const share = await this.prisma.share.findUnique({
where: { id: shareId },
@@ -39,8 +45,8 @@ export class FileService {
let diskFileSize: number;
try {
diskFileSize = fs.statSync(
`${SHARE_DIRECTORY}/${shareId}/${file.id}.tmp-chunk`,
diskFileSize = (
await fs.stat(`${SHARE_DIRECTORY}/${shareId}/${file.id}.tmp-chunk`)
).size;
} catch {
diskFileSize = 0;
@@ -59,6 +65,13 @@ export class FileService {
const buffer = Buffer.from(data, "base64");
// Check if there is enough space on the server
const space = await fs.statfs(SHARE_DIRECTORY);
const availableSpace = space.bavail * space.bsize;
if (availableSpace < buffer.byteLength) {
throw new InternalServerErrorException("Not enough space on the server");
}
// Check if share size limit is exceeded
const fileSizeSum = share.files.reduce(
(n, { size }) => n + parseInt(size),
@@ -78,19 +91,19 @@ export class FileService {
);
}
fs.appendFileSync(
await fs.appendFile(
`${SHARE_DIRECTORY}/${shareId}/${file.id}.tmp-chunk`,
buffer,
);
const isLastChunk = chunk.index == chunk.total - 1;
if (isLastChunk) {
fs.renameSync(
await fs.rename(
`${SHARE_DIRECTORY}/${shareId}/${file.id}.tmp-chunk`,
`${SHARE_DIRECTORY}/${shareId}/${file.id}`,
);
const fileSize = fs.statSync(
`${SHARE_DIRECTORY}/${shareId}/${file.id}`,
const fileSize = (
await fs.stat(`${SHARE_DIRECTORY}/${shareId}/${file.id}`)
).size;
await this.prisma.file.create({
data: {
@@ -112,7 +125,7 @@ export class FileService {
if (!fileMetaData) throw new NotFoundException("File not found");
const file = fs.createReadStream(`${SHARE_DIRECTORY}/${shareId}/${fileId}`);
const file = createReadStream(`${SHARE_DIRECTORY}/${shareId}/${fileId}`);
return {
metaData: {
@@ -131,19 +144,19 @@ export class FileService {
if (!fileMetaData) throw new NotFoundException("File not found");
fs.unlinkSync(`${SHARE_DIRECTORY}/${shareId}/${fileId}`);
await fs.unlink(`${SHARE_DIRECTORY}/${shareId}/${fileId}`);
await this.prisma.file.delete({ where: { id: fileId } });
}
async deleteAllFiles(shareId: string) {
await fs.promises.rm(`${SHARE_DIRECTORY}/${shareId}`, {
await fs.rm(`${SHARE_DIRECTORY}/${shareId}`, {
recursive: true,
force: true,
});
}
getZip(shareId: string) {
return fs.createReadStream(`${SHARE_DIRECTORY}/${shareId}/archive.zip`);
return createReadStream(`${SHARE_DIRECTORY}/${shareId}/archive.zip`);
}
}

View File

@@ -9,14 +9,16 @@ import * as moment from "moment";
import { PrismaService } from "src/prisma/prisma.service";
import { ShareSecurityGuard } from "src/share/guard/shareSecurity.guard";
import { ShareService } from "src/share/share.service";
import { ConfigService } from "src/config/config.service";
@Injectable()
export class FileSecurityGuard extends ShareSecurityGuard {
constructor(
private _shareService: ShareService,
private _prisma: PrismaService,
_config: ConfigService,
) {
super(_shareService, _prisma);
super(_shareService, _prisma, _config);
}
async canActivate(context: ExecutionContext) {

View File

@@ -1,6 +1,7 @@
import {
ClassSerializerInterceptor,
Logger,
LogLevel,
ValidationPipe,
} from "@nestjs/common";
import { NestFactory, Reflector } from "@nestjs/core";
@@ -12,10 +13,35 @@ import { NextFunction, Request, Response } from "express";
import * as fs from "fs";
import { AppModule } from "./app.module";
import { ConfigService } from "./config/config.service";
import { DATA_DIRECTORY } from "./constants";
import {
DATA_DIRECTORY,
LOG_LEVEL_AVAILABLE,
LOG_LEVEL_DEFAULT,
LOG_LEVEL_ENV,
} from "./constants";
function generateNestJsLogLevels(): LogLevel[] {
if (LOG_LEVEL_ENV) {
const levelIndex = LOG_LEVEL_AVAILABLE.indexOf(LOG_LEVEL_ENV as any);
if (levelIndex === -1) {
throw new Error(`log level ${LOG_LEVEL_ENV} unknown`);
}
return LOG_LEVEL_AVAILABLE.slice(levelIndex, LOG_LEVEL_AVAILABLE.length);
} else {
const levelIndex = LOG_LEVEL_AVAILABLE.indexOf(LOG_LEVEL_DEFAULT);
return LOG_LEVEL_AVAILABLE.slice(levelIndex, LOG_LEVEL_AVAILABLE.length);
}
}
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule);
const logLevels = generateNestJsLogLevels();
Logger.log(`Showing ${logLevels.join(", ")} messages`);
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
logger: logLevels,
});
app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));
@@ -48,7 +74,9 @@ async function bootstrap() {
SwaggerModule.setup("api/swagger", app, document);
}
await app.listen(parseInt(process.env.PORT) || 8080);
await app.listen(
parseInt(process.env.BACKEND_PORT || process.env.PORT || "8080"),
);
const logger = new Logger("UnhandledAsyncError");
process.on("unhandledRejection", (e) => logger.error(e));

View File

@@ -3,4 +3,6 @@ export interface OAuthSignInDto {
providerId: string;
providerUsername: string;
email: string;
isAdmin?: boolean;
idToken?: string;
}

View File

@@ -85,7 +85,7 @@ export class OAuthController {
accessToken?: string;
refreshToken?: string;
loginToken?: string;
} = await this.oauthService.signIn(user);
} = await this.oauthService.signIn(user, request.ip);
if (token.accessToken) {
this.authService.addTokensToResponse(
response,

View File

@@ -1,4 +1,4 @@
import { Module } from "@nestjs/common";
import { forwardRef, Module } from "@nestjs/common";
import { OAuthController } from "./oauth.controller";
import { OAuthService } from "./oauth.service";
import { AuthModule } from "../auth/auth.module";
@@ -51,6 +51,7 @@ import { MicrosoftProvider } from "./provider/microsoft.provider";
inject: ["OAUTH_PROVIDERS"],
},
],
imports: [AuthModule],
imports: [forwardRef(() => AuthModule)],
exports: [OAuthService],
})
export class OAuthModule {}

View File

@@ -1,4 +1,4 @@
import { Inject, Injectable } from "@nestjs/common";
import { forwardRef, Inject, Injectable, Logger } from "@nestjs/common";
import { User } from "@prisma/client";
import { nanoid } from "nanoid";
import { AuthService } from "../auth/auth.service";
@@ -6,15 +6,19 @@ import { ConfigService } from "../config/config.service";
import { PrismaService } from "../prisma/prisma.service";
import { OAuthSignInDto } from "./dto/oauthSignIn.dto";
import { ErrorPageException } from "./exceptions/errorPage.exception";
import { OAuthProvider } from "./provider/oauthProvider.interface";
@Injectable()
export class OAuthService {
constructor(
private prisma: PrismaService,
private config: ConfigService,
private auth: AuthService,
@Inject(forwardRef(() => AuthService)) private auth: AuthService,
@Inject("OAUTH_PLATFORMS") private platforms: string[],
@Inject("OAUTH_PROVIDERS")
private oAuthProviders: Record<string, OAuthProvider<unknown>>,
) {}
private readonly logger = new Logger(OAuthService.name);
available(): string[] {
return this.platforms
@@ -26,6 +30,18 @@ export class OAuthService {
.map(([platform, _]) => platform);
}
availableProviders(): Record<string, OAuthProvider<unknown>> {
return Object.fromEntries(
Object.entries(this.oAuthProviders)
.map(([providerName, provider]) => [
[providerName, provider],
this.config.get(`oauth.${providerName}-enabled`),
])
.filter(([_, enabled]) => enabled)
.map(([provider, _]) => provider),
);
}
async status(user: User) {
const oauthUsers = await this.prisma.oAuthUser.findMany({
select: {
@@ -39,21 +55,25 @@ export class OAuthService {
return Object.fromEntries(oauthUsers.map((u) => [u.provider, u]));
}
async signIn(user: OAuthSignInDto) {
async signIn(user: OAuthSignInDto, ip: string) {
const oauthUser = await this.prisma.oAuthUser.findFirst({
where: {
provider: user.provider,
providerUserId: user.providerId,
},
include: {
user: true,
},
});
if (oauthUser) {
return this.auth.generateToken(oauthUser.user, true);
await this.updateIsAdmin(user);
const updatedUser = await this.prisma.user.findFirst({
where: {
id: oauthUser.userId,
},
});
this.logger.log(`Successful login for user ${user.email} from IP ${ip}`);
return this.auth.generateToken(updatedUser, { idToken: user.idToken });
}
return this.signUp(user);
return this.signUp(user, ip);
}
async link(
@@ -103,8 +123,10 @@ export class OAuthService {
}
private async getAvailableUsername(preferredUsername: string) {
// only remove + and - from preferred username for now (maybe not enough)
let username = preferredUsername.replace(/[+-]/g, "").substring(0, 20);
// Only keep letters, numbers, dots, and underscores. Truncate to 20 characters.
let username = preferredUsername
.replace(/[^a-zA-Z0-9._]/g, "")
.substring(0, 20);
while (true) {
const user = await this.prisma.user.findFirst({
where: {
@@ -119,7 +141,7 @@ export class OAuthService {
}
}
private async signUp(user: OAuthSignInDto) {
private async signUp(user: OAuthSignInDto, ip: string) {
// register
if (!this.config.get("oauth.allowRegistration")) {
throw new ErrorPageException("no_user", "/auth/signIn", [
@@ -148,14 +170,19 @@ export class OAuthService {
userId: existingUser.id,
},
});
return this.auth.generateToken(existingUser, true);
await this.updateIsAdmin(user);
return this.auth.generateToken(existingUser, { idToken: user.idToken });
}
const result = await this.auth.signUp({
email: user.email,
username: await this.getAvailableUsername(user.providerUsername),
password: null,
});
const result = await this.auth.signUp(
{
email: user.email,
username: await this.getAvailableUsername(user.providerUsername),
password: null,
},
ip,
user.isAdmin,
);
await this.prisma.oAuthUser.create({
data: {
@@ -168,4 +195,16 @@ export class OAuthService {
return result;
}
private async updateIsAdmin(user: OAuthSignInDto) {
if ("isAdmin" in user)
await this.prisma.user.update({
where: {
email: user.email,
},
data: {
isAdmin: user.isAdmin,
},
});
}
}

View File

@@ -81,12 +81,17 @@ export class DiscordProvider implements OAuthProvider<DiscordToken> {
if (guild) {
await this.checkLimitedGuild(token, guild);
}
const limitedUsers = this.config.get("oauth.discord-limitedUsers");
if (limitedUsers) {
await this.checkLimitedUsers(user, limitedUsers);
}
return {
provider: "discord",
providerId: user.id,
providerUsername: user.global_name ?? user.username,
email: user.email,
idToken: `discord:${token.idToken}`,
};
}
@@ -101,10 +106,16 @@ export class DiscordProvider implements OAuthProvider<DiscordToken> {
});
const guilds = (await res.json()) as DiscordPartialGuild[];
if (!guilds.some((guild) => guild.id === guildId)) {
throw new ErrorPageException("discord_guild_permission_denied");
throw new ErrorPageException("user_not_allowed");
}
} catch {
throw new ErrorPageException("discord_guild_permission_denied");
throw new ErrorPageException("user_not_allowed");
}
}
async checkLimitedUsers(user: DiscordUser, userIds: string) {
if (!userIds.split(",").includes(user.id)) {
throw new ErrorPageException("user_not_allowed");
}
}
}

View File

@@ -1,12 +1,13 @@
import { Logger } from "@nestjs/common";
import { ConfigService } from "../../config/config.service";
import { InternalServerErrorException, Logger } from "@nestjs/common";
import { JwtService } from "@nestjs/jwt";
import { Cache } from "cache-manager";
import * as jmespath from "jmespath";
import { nanoid } from "nanoid";
import { ConfigService } from "../../config/config.service";
import { OAuthCallbackDto } from "../dto/oauthCallback.dto";
import { OAuthProvider, OAuthToken } from "./oauthProvider.interface";
import { OAuthSignInDto } from "../dto/oauthSignIn.dto";
import { ErrorPageException } from "../exceptions/errorPage.exception";
import { OAuthProvider, OAuthToken } from "./oauthProvider.interface";
export abstract class GenericOidcProvider implements OAuthProvider<OidcToken> {
protected discoveryUri: string;
@@ -69,7 +70,7 @@ export abstract class GenericOidcProvider implements OAuthProvider<OidcToken> {
new URLSearchParams({
client_id: this.config.get(`oauth.${this.name}-clientId`),
response_type: "code",
scope: "openid profile email",
scope: this.name == "oidc" ? this.config.get(`oauth.oidc-scope`) : "openid email profile",
redirect_uri: this.getRedirectUri(),
state,
nonce,
@@ -108,9 +109,20 @@ export abstract class GenericOidcProvider implements OAuthProvider<OidcToken> {
token: OAuthToken<OidcToken>,
query: OAuthCallbackDto,
claim?: string,
roleConfig?: {
path?: string;
generalAccess?: string;
adminAccess?: string;
},
): Promise<OAuthSignInDto> {
const idTokenData = this.decodeIdToken(token.idToken);
// maybe it's not necessary to verify the id token since it's directly obtained from the provider
if (!idTokenData) {
this.logger.error(
`Can not get ID Token from response ${JSON.stringify(token.rawToken, undefined, 2)}`,
);
throw new InternalServerErrorException();
}
const key = `oauth-${this.name}-nonce-${query.state}`;
const nonce = await this.cache.get(key);
@@ -128,6 +140,44 @@ export abstract class GenericOidcProvider implements OAuthProvider<OidcToken> {
idTokenData.name ||
idTokenData.nickname;
let isAdmin: boolean;
if (roleConfig?.path) {
// A path to read roles from the token is configured
let roles: string[] | null;
try {
roles = jmespath.search(idTokenData, roleConfig.path);
} catch (e) {
roles = null;
}
if (Array.isArray(roles)) {
// Roles are found in the token
if (
roleConfig.generalAccess &&
!roles.includes(roleConfig.generalAccess)
) {
// Role for general access is configured and the user does not have it
this.logger.error(
`User roles ${roles} do not include ${roleConfig.generalAccess}`,
);
throw new ErrorPageException("user_not_allowed");
}
if (roleConfig.adminAccess) {
// Role for admin access is configured
isAdmin = roles.includes(roleConfig.adminAccess);
}
} else {
this.logger.error(
`Roles not found at path ${roleConfig.path} in ID Token ${JSON.stringify(
idTokenData,
undefined,
2,
)}`,
);
throw new ErrorPageException("user_not_allowed");
}
}
if (!username) {
this.logger.error(
`Can not get username from ID Token ${JSON.stringify(
@@ -146,6 +196,8 @@ export abstract class GenericOidcProvider implements OAuthProvider<OidcToken> {
email: idTokenData.email,
providerId: idTokenData.sub,
providerUsername: username,
...(isAdmin !== undefined && { isAdmin }),
idToken: `${this.name}:${token.idToken}`,
};
}
@@ -200,6 +252,8 @@ export interface OidcConfiguration {
id_token_signing_alg_values_supported: string[];
scopes_supported?: string[];
claims_supported?: string[];
frontchannel_logout_supported?: boolean;
end_session_endpoint?: string;
}
export interface OidcJwk {

View File

@@ -61,6 +61,7 @@ export class GitHubProvider implements OAuthProvider<GitHubToken> {
providerId: user.id.toString(),
providerUsername: user.name ?? user.login,
email,
idToken: `github:${token.idToken}`,
};
}

View File

@@ -34,6 +34,15 @@ export class OidcProvider extends GenericOidcProvider {
_?: string,
): Promise<OAuthSignInDto> {
const claim = this.config.get("oauth.oidc-usernameClaim") || undefined;
return super.getUserInfo(token, query, claim);
const rolePath = this.config.get("oauth.oidc-rolePath") || undefined;
const roleGeneralAccess =
this.config.get("oauth.oidc-roleGeneralAccess") || undefined;
const roleAdminAccess =
this.config.get("oauth.oidc-roleAdminAccess") || undefined;
return super.getUserInfo(token, query, claim, {
path: rolePath,
generalAccess: roleGeneralAccess,
adminAccess: roleAdminAccess,
});
}
}

View File

@@ -1,9 +1,11 @@
import { Injectable } from "@nestjs/common";
import { Injectable, Logger } from "@nestjs/common";
import { PrismaClient } from "@prisma/client";
import { DATABASE_URL } from "../constants";
@Injectable()
export class PrismaService extends PrismaClient {
private readonly logger = new Logger(PrismaService.name);
constructor() {
super({
datasources: {
@@ -12,6 +14,6 @@ export class PrismaService extends PrismaClient {
},
},
});
super.$connect().then(() => console.info("Connected to the database"));
super.$connect().then(() => this.logger.log("Connected to the database"));
}
}

View File

@@ -13,4 +13,10 @@ export class CreateReverseShareDTO {
@Min(1)
@Max(1000)
maxUseCount: number;
@IsBoolean()
simplified: boolean;
@IsBoolean()
publicAccess: boolean;
}

View File

@@ -13,6 +13,9 @@ export class ReverseShareDTO {
@Expose()
token: string;
@Expose()
simplified: boolean;
from(partial: Partial<ReverseShareDTO>) {
return plainToClass(ReverseShareDTO, partial, {
excludeExtraneousValues: true,

View File

@@ -49,6 +49,8 @@ export class ReverseShareService {
remainingUses: data.maxUseCount,
maxShareSize: data.maxShareSize,
sendEmailNotification: data.sendEmailNotification,
simplified: data.simplified,
publicAccess: data.publicAccess,
creatorId,
},
});

View File

@@ -0,0 +1,19 @@
import { Expose, plainToClass } from "class-transformer";
import { ShareDTO } from "./share.dto";
export class CompletedShareDTO extends ShareDTO {
@Expose()
notifyReverseShareCreator?: boolean;
from(partial: Partial<CompletedShareDTO>) {
return plainToClass(CompletedShareDTO, partial, {
excludeExtraneousValues: true,
});
}
fromList(partial: Partial<CompletedShareDTO>[]) {
return partial.map((part) =>
plainToClass(CompletedShareDTO, part, { excludeExtraneousValues: true }),
);
}
}

View File

@@ -1,5 +1,4 @@
import {
CanActivate,
ExecutionContext,
ForbiddenException,
Injectable,
@@ -9,13 +8,19 @@ import { Request } from "express";
import * as moment from "moment";
import { PrismaService } from "src/prisma/prisma.service";
import { ShareService } from "src/share/share.service";
import { ConfigService } from "src/config/config.service";
import { JwtGuard } from "src/auth/guard/jwt.guard";
import { User } from "@prisma/client";
@Injectable()
export class ShareSecurityGuard implements CanActivate {
export class ShareSecurityGuard extends JwtGuard {
constructor(
private shareService: ShareService,
private prisma: PrismaService,
) {}
configService: ConfigService,
) {
super(configService);
}
async canActivate(context: ExecutionContext) {
const request: Request = context.switchToHttp().getRequest();
@@ -31,7 +36,7 @@ export class ShareSecurityGuard implements CanActivate {
const share = await this.prisma.share.findUnique({
where: { id: shareId },
include: { security: true },
include: { security: true, reverseShare: true },
});
if (
@@ -53,6 +58,22 @@ export class ShareSecurityGuard implements CanActivate {
"share_token_required",
);
// Run the JWTGuard to set the user
await super.canActivate(context);
const user = request.user as User;
// Only the creator and reverse share creator can access the reverse share if it's not public
if (
share.reverseShare &&
!share.reverseShare.publicAccess &&
share.creatorId !== user?.id &&
share.reverseShare.creatorId !== user?.id
)
throw new ForbiddenException(
"Only reverse share creator can access this share",
"private_share",
);
return true;
}
}

View File

@@ -10,9 +10,11 @@ import {
Res,
UseGuards,
} from "@nestjs/common";
import { JwtService } from "@nestjs/jwt";
import { Throttle } from "@nestjs/throttler";
import { User } from "@prisma/client";
import { Request, Response } from "express";
import * as moment from "moment";
import { GetUser } from "src/auth/decorator/getUser.decorator";
import { AdministratorGuard } from "src/auth/guard/isAdmin.guard";
import { JwtGuard } from "src/auth/guard/jwt.guard";
@@ -27,9 +29,13 @@ import { ShareOwnerGuard } from "./guard/shareOwner.guard";
import { ShareSecurityGuard } from "./guard/shareSecurity.guard";
import { ShareTokenSecurity } from "./guard/shareTokenSecurity.guard";
import { ShareService } from "./share.service";
import { CompletedShareDTO } from "./dto/shareComplete.dto";
@Controller("shares")
export class ShareController {
constructor(private shareService: ShareService) {}
constructor(
private shareService: ShareService,
private jwtService: JwtService,
) {}
@Get("all")
@UseGuards(JwtGuard, AdministratorGuard)
@@ -81,7 +87,7 @@ export class ShareController {
@UseGuards(CreateShareGuard, ShareOwnerGuard)
async complete(@Param("id") id: string, @Req() request: Request) {
const { reverse_share_token } = request.cookies;
return new ShareDTO().from(
return new CompletedShareDTO().from(
await this.shareService.complete(id, reverse_share_token),
);
}
@@ -121,10 +127,13 @@ export class ShareController {
@Post(":id/token")
async getShareToken(
@Param("id") id: string,
@Req() request: Request,
@Res({ passthrough: true }) response: Response,
@Body() body: SharePasswordDto,
) {
const token = await this.shareService.getShareToken(id, body.password);
this.clearShareTokenCookies(request, response);
response.cookie(`share_${id}_token`, token, {
path: "/",
httpOnly: true,
@@ -132,4 +141,32 @@ export class ShareController {
return { token };
}
/**
* Keeps the 10 most recent share token cookies and deletes the rest and all expired ones
*/
private clearShareTokenCookies(request: Request, response: Response) {
const shareTokenCookies = Object.entries(request.cookies)
.filter(([key]) => key.startsWith("share_") && key.endsWith("_token"))
.map(([key, value]) => ({
key,
payload: this.jwtService.decode(value),
}));
const expiredTokens = shareTokenCookies.filter(
(cookie) => cookie.payload.exp < moment().unix(),
);
const validTokens = shareTokenCookies.filter(
(cookie) => cookie.payload.exp >= moment().unix(),
);
expiredTokens.forEach((cookie) => response.clearCookie(cookie.key));
if (validTokens.length > 10) {
validTokens
.sort((a, b) => a.payload.exp - b.payload.exp)
.slice(0, -10)
.forEach((cookie) => response.clearCookie(cookie.key));
}
}
}

View File

@@ -4,7 +4,7 @@ import {
Injectable,
NotFoundException,
} from "@nestjs/common";
import { JwtService } from "@nestjs/jwt";
import { JwtService, JwtSignOptions } from "@nestjs/jwt";
import { Share, User } from "@prisma/client";
import * as archiver from "archiver";
import * as argon from "argon2";
@@ -159,11 +159,12 @@ export class ShareService {
);
}
if (
share.reverseShare &&
this.config.get("smtp.enabled") &&
share.reverseShare.sendEmailNotification
) {
const notifyReverseShareCreator = share.reverseShare
? this.config.get("smtp.enabled") &&
share.reverseShare.sendEmailNotification
: undefined;
if (notifyReverseShareCreator) {
await this.emailService.sendMailToReverseShareCreator(
share.reverseShare.creator.email,
share.id,
@@ -180,10 +181,15 @@ export class ShareService {
});
}
return this.prisma.share.update({
const updatedShare = await this.prisma.share.update({
where: { id },
data: { uploadLocked: true },
});
return {
...updatedShare,
notifyReverseShareCreator,
};
}
async revertComplete(id: string) {
@@ -239,7 +245,11 @@ export class ShareService {
const share = await this.prisma.share.findUnique({
where: { id },
include: {
files: true,
files: {
orderBy: {
name: "asc",
},
},
creator: true,
security: true,
},
@@ -305,11 +315,21 @@ export class ShareService {
},
});
if (
share?.security?.password &&
!(await argon.verify(share.security.password, password))
) {
throw new ForbiddenException("Wrong password", "wrong_password");
if (share?.security?.password) {
if (!password) {
throw new ForbiddenException(
"This share is password protected",
"share_password_required",
);
}
const isPasswordValid = await argon.verify(
share.security.password,
password,
);
if (!isPasswordValid) {
throw new ForbiddenException("Wrong password", "wrong_password");
}
}
if (share.security?.maxViews && share.security.maxViews <= share.views) {
@@ -325,22 +345,29 @@ export class ShareService {
}
async generateShareToken(shareId: string) {
const { expiration } = await this.prisma.share.findUnique({
const { expiration, createdAt } = await this.prisma.share.findUnique({
where: { id: shareId },
});
return this.jwtService.sign(
{
shareId,
},
{
expiresIn: moment(expiration).diff(new Date(), "seconds") + "s",
secret: this.config.get("internal.jwtSecret"),
},
);
const tokenPayload = {
shareId,
shareCreatedAt: moment(createdAt).unix(),
iat: moment().unix(),
};
const tokenOptions: JwtSignOptions = {
secret: this.config.get("internal.jwtSecret"),
};
if (!moment(expiration).isSame(0)) {
tokenOptions.expiresIn = moment(expiration).diff(new Date(), "seconds");
}
return this.jwtService.sign(tokenPayload, tokenOptions);
}
async verifyShareToken(shareId: string, token: string) {
const { expiration } = await this.prisma.share.findUnique({
const { expiration, createdAt } = await this.prisma.share.findUnique({
where: { id: shareId },
});
@@ -351,7 +378,10 @@ export class ShareService {
ignoreExpiration: moment(expiration).isSame(0),
});
return claims.shareId == shareId;
return (
claims.shareId == shareId &&
claims.shareCreatedAt == moment(createdAt).unix()
);
} catch {
return false;
}

View File

@@ -25,16 +25,23 @@ export class UserDTO {
@Expose()
isAdmin: boolean;
@Expose()
isLdap: boolean;
ldapDN?: string;
@Expose()
totpVerified: boolean;
from(partial: Partial<UserDTO>) {
return plainToClass(UserDTO, partial, { excludeExtraneousValues: true });
const result = plainToClass(UserDTO, partial, {
excludeExtraneousValues: true,
});
result.isLdap = partial.ldapDN?.length > 0;
return result;
}
fromList(partial: Partial<UserDTO>[]) {
return partial.map((part) =>
plainToClass(UserDTO, part, { excludeExtraneousValues: true }),
);
return partial.map((part) => this.from(part));
}
}

View File

@@ -3,6 +3,7 @@ import {
Controller,
Delete,
Get,
HttpCode,
Param,
Patch,
Post,
@@ -14,6 +15,7 @@ import { Response } from "express";
import { GetUser } from "src/auth/decorator/getUser.decorator";
import { AdministratorGuard } from "src/auth/guard/isAdmin.guard";
import { JwtGuard } from "src/auth/guard/jwt.guard";
import { ConfigService } from "../config/config.service";
import { CreateUserDTO } from "./dto/createUser.dto";
import { UpdateOwnUserDTO } from "./dto/updateOwnUser.dto";
import { UpdateUserDto } from "./dto/updateUser.dto";
@@ -22,7 +24,10 @@ import { UserSevice } from "./user.service";
@Controller("users")
export class UserController {
constructor(private userService: UserSevice) {}
constructor(
private userService: UserSevice,
private config: ConfigService,
) {}
// Own user operations
@Get("me")
@@ -44,18 +49,26 @@ export class UserController {
}
@Delete("me")
@HttpCode(204)
@UseGuards(JwtGuard)
async deleteCurrentUser(
@GetUser() user: User,
@Res({ passthrough: true }) response: Response,
) {
response.cookie("access_token", "accessToken", { maxAge: -1 });
await this.userService.delete(user.id);
const isSecure = this.config.get("general.secureCookies");
response.cookie("access_token", "accessToken", {
maxAge: -1,
secure: isSecure,
});
response.cookie("refresh_token", "", {
path: "/api/auth/token",
httpOnly: true,
maxAge: -1,
secure: isSecure,
});
return new UserDTO().from(await this.userService.delete(user.id));
}
// Global user operations

View File

@@ -8,5 +8,6 @@ import { FileModule } from "src/file/file.module";
imports: [EmailModule, FileModule],
providers: [UserSevice],
controllers: [UserController],
exports: [UserSevice],
})
export class UserModule {}

View File

@@ -1,19 +1,26 @@
import { BadRequestException, Injectable } from "@nestjs/common";
import { BadRequestException, Injectable, Logger } from "@nestjs/common";
import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library";
import * as argon from "argon2";
import * as crypto from "crypto";
import { Entry } from "ldapts";
import { AuthSignInDTO } from "src/auth/dto/authSignIn.dto";
import { EmailService } from "src/email/email.service";
import { PrismaService } from "src/prisma/prisma.service";
import { inspect } from "util";
import { ConfigService } from "../config/config.service";
import { FileService } from "../file/file.service";
import { CreateUserDTO } from "./dto/createUser.dto";
import { UpdateUserDto } from "./dto/updateUser.dto";
@Injectable()
export class UserSevice {
private readonly logger = new Logger(UserSevice.name);
constructor(
private prisma: PrismaService,
private emailService: EmailService,
private fileService: FileService,
private configService: ConfigService,
) {}
async list() {
@@ -82,10 +89,140 @@ export class UserSevice {
});
if (!user) throw new BadRequestException("User not found");
if (user.isAdmin) {
const userCount = await this.prisma.user.count({
where: { isAdmin: true },
});
if (userCount === 1) {
throw new BadRequestException("Cannot delete the last admin user");
}
}
await Promise.all(
user.shares.map((share) => this.fileService.deleteAllFiles(share.id)),
);
return await this.prisma.user.delete({ where: { id } });
}
async findOrCreateFromLDAP(
providedCredentials: AuthSignInDTO,
ldapEntry: Entry,
) {
const fieldNameMemberOf = this.configService.get("ldap.fieldNameMemberOf");
const fieldNameEmail = this.configService.get("ldap.fieldNameEmail");
let isAdmin = false;
if (fieldNameMemberOf in ldapEntry) {
const adminGroup = this.configService.get("ldap.adminGroups");
const entryGroups = Array.isArray(ldapEntry[fieldNameMemberOf])
? ldapEntry[fieldNameMemberOf]
: [ldapEntry[fieldNameMemberOf]];
isAdmin = entryGroups.includes(adminGroup) ?? false;
} else {
this.logger.warn(
`Trying to create/update a ldap user but the member field ${fieldNameMemberOf} is not present.`,
);
}
let userEmail: string | null = null;
if (fieldNameEmail in ldapEntry) {
const value = Array.isArray(ldapEntry[fieldNameEmail])
? ldapEntry[fieldNameEmail][0]
: ldapEntry[fieldNameEmail];
if (value) {
userEmail = value.toString();
}
} else {
this.logger.warn(
`Trying to create/update a ldap user but the email field ${fieldNameEmail} is not present.`,
);
}
if (providedCredentials.email) {
/* if LDAP does not provides an users email address, take the user provided email address instead */
userEmail = providedCredentials.email;
}
const randomId = crypto.randomUUID();
const placeholderUsername = `ldap_user_${randomId}`;
const placeholderEMail = `${randomId}@ldap.local`;
try {
const user = await this.prisma.user.upsert({
create: {
username: providedCredentials.username ?? placeholderUsername,
email: userEmail ?? placeholderEMail,
password: await argon.hash(crypto.randomUUID()),
isAdmin,
ldapDN: ldapEntry.dn,
},
update: {
isAdmin,
ldapDN: ldapEntry.dn,
},
where: {
ldapDN: ldapEntry.dn,
},
});
if (user.username === placeholderUsername) {
/* Give the user a human readable name if the user has been created with a placeholder username */
await this.prisma.user
.update({
where: {
id: user.id,
},
data: {
username: `user_${user.id}`,
},
})
.then((newUser) => {
user.username = newUser.username;
})
.catch((error) => {
this.logger.warn(
`Failed to update users ${user.id} placeholder username: ${inspect(error)}`,
);
});
}
if (userEmail && userEmail !== user.email) {
/* Sync users email if it has changed */
await this.prisma.user
.update({
where: {
id: user.id,
},
data: {
email: userEmail,
},
})
.then((newUser) => {
this.logger.log(
`Updated users ${user.id} email from ldap from ${user.email} to ${userEmail}.`,
);
user.email = newUser.email;
})
.catch((error) => {
this.logger.error(
`Failed to update users ${user.id} email to ${userEmail}: ${inspect(error)}`,
);
});
}
return user;
} catch (e) {
if (e instanceof PrismaClientKnownRequestError) {
if (e.code == "P2002") {
const duplicatedField: string = e.meta.target[0];
throw new BadRequestException(
`A user with this ${duplicatedField} already exists`,
);
}
}
}
}
}

View File

@@ -1,4 +1,3 @@
version: '3.8'
services:
clamav:
restart: unless-stopped

View File

@@ -1,19 +1,14 @@
version: '3.8'
services:
pingvin-share:
image: stonith404/pingvin-share
restart: unless-stopped
ports:
- 3000:3000
environment:
- TRUST_PROXY=false # Set to true if a reverse proxy is in front of the container
volumes:
- "./data:/opt/app/backend/data"
- "./data/images:/opt/app/frontend/public/img"
# Optional: If you add ClamAV, uncomment the following to have ClamAV start first.
# depends_on:
# clamav:
# condition: service_healthy
# Optional: Add ClamAV (see README.md)
# ClamAV is currently only available for AMD64 see https://github.com/Cisco-Talos/clamav/issues/482
# clamav:
# restart: unless-stopped
# image: clamav/clamav
# To add ClamAV, to scan your shares for malicious files,
# see https://stonith404.github.io/pingvin-share/setup/integrations/#clamav-docker-only

View File

@@ -1,95 +0,0 @@
_Leer esto en otro idioma: [Inglés](/CONTRIBUTING.md), [Español](/docs/CONTRIBUTING.es.md), [Chino Simplificado](/docs/CONTRIBUTING.zh-cn.md)_
---
# Contribuyendo
¡Nos ❤️ encantaría que contribuyas a Pingvin Share y nos ayudes a hacerlo mejor! Todas las contribuciones son bienvenidas, incluyendo problemas, sugerencias, _pull requests_ y más.
## Para comenzar
Si encontraste un error, tienes una sugerencia o algo más, simplemente crea un problema (issue) en GitHub y nos pondremos en contacto contigo 😊.
## Para hacer una Pull Request
Antes de enviar la pull request para su revisión, asegúrate de que:
- El nombre de la pull request sigue las [especificaciones de Commits Convencionales](https://www.conventionalcommits.org/):
`<tipo>[ámbito opcional]: <descripción>`
ejemplo:
```
feat(share): agregar protección con contraseña
```
Donde `tipo` puede ser:
- **feat** - es una nueva función
- **doc** - cambios solo en la documentación
- **fix** - una corrección de error
- **refactor** - cambios en el código que no solucionan un error ni agregan una función
- Tu pull requests tiene una descripción detallada.
- Ejecutaste `npm run format` para formatear el código.
<details>
<summary>¿No sabes como crear una pull request? Aprende cómo crear una pull request</summary>
1. Crea un fork del repositorio haciendo clic en el botón `Fork` en el repositorio de Pingvin Share.
2. Clona tu fork en tu máquina con `git clone`.
```
$ git clone https://github.com/[your_username]/pingvin-share
```
3. Trabajar - hacer commit - repetir
4. Haz un `push` de tus cambios a GitHub.
```
$ git push origin [nombre_de_tu_nueva_rama]
```
5. Envía tus cambios para su revisión. Si vas a tu repositorio en GitHub, verás un botón `Comparar y crear pull requests`. Haz clic en ese botón.
6. Inicia una Pull Request
7. Ahora envía la pull requests y haz clic en `Crear pull requests`
8. Espera a que alguien revise tu solicitud y apruebe o rechace tus cambios. Puedes ver los comentarios en la página de la solicitud en GitHub.
</details>
## Instalación del proyecto
Pingvin Share consiste de un frontend y un backend.
### Backend
El backend está hecho con [Nest.js](https://nestjs.com) y usa Typescript.
#### Instalación
1. Abrimos la carpeta `backend`
2. Instalamos las dependencias con `npm install`
3. Haz un `push` del esquema de la base de datos a la base de datos ejecutando `npx prisma db push`
4. Rellena la base de datos ejecutando `npx prisma db seed`
5. Inicia el backend con `npm run dev`
### Frontend
El frontend está hecho con [Next.js](https://nextjs.org) y usa Typescript.
#### Instalación
1. Primero inicia el backend
2. Abre la carpeta `frontend`
3. Instala las dependencias con `npm install`
4. Inicia el frontend con `npm run dev`
¡Ya está todo listo!
### Testing
Por el momento, solo tenemos pruebas para el backend. Para ejecutar estas pruebas, debes ejecutar el comando `npm run test:system` en la carpeta del backend.

View File

@@ -1,97 +0,0 @@
_选择合适的语言阅读: [西班牙语](/docs/CONTRIBUTING.es.md), [英语](/CONTRIBUTING.md), [简体中文](/docs/CONTRIBUTING.zh-cn.md)_
---
# 提交贡献
我们非常感谢你 ❤️ 为 Pingvin Share 提交贡献使其变得更棒! 欢迎任何形式的贡献,包括 issues, 建议, PRs 和其他形式
## 小小的开始
你找到了一个 bug有新特性建议或者其他提议请在 GitHub 建立一个 issue 以便我和你联络 😊
## 提交一个 Pull Request
在你提交 PR 前请确保
- PR 的名字遵守 [Conventional Commits specification](https://www.conventionalcommits.org):
`<type>[optional scope]: <description>`
例如:
```
feat(share): add password protection
```
`TYPE` 可以是:
- **feat** - 这是一个新特性 feature
- **doc** - 仅仅改变了文档部分 documentation
- **fix** - 修复了一个 bug
- **refactor** - 更新了代码,但是并非出于增加新特性 feature 或修复 bug 的目的
- 请在 PR 中附详细的解释说明
- 使用 `npm run format` 格式化你的代码
<details>
<summary>不知道怎么发起一个 PR 点开了解怎么发起一个 PR </summary>
1. 点击 Pingvin Share 仓库的 `Fork` 按钮,复制一份你的仓库
2. 通过 `git clone` 将你的仓库克隆到本地
```
$ git clone https://github.com/[你的用户名]/pingvin-share
```
3. 进行你的修改 - 提交 commit 你的修改 - 重复直到完成
4. 将你的修改提交到 GitHub
```
$ git push origin [你的新分支的名字]
```
5. 提交你的代码以便代码审查
如果你进入你 fork 的 Github 仓库,你会看到一个 `Compare & pull request` 按钮,点击该按钮
6. 发起一个 PR
7. 点击 `Create pull request` 来提交你的 PR
8. 等待代码审查,通过或以某些原因拒绝
</details>
## 配置开发项目
Pingvin Share 包括前端和后端部分
### 后端
后端使用 [Nest.js](https://nestjs.com) 建立,使用 Typescript
#### 搭建
1. 打开 `backend` 文件夹
2. 使用 `npm install` 安装依赖
3. 通过 `npx prisma db push` 配置数据库结构
4. 通过 `npx prisma db seed` 初始化数据库数据
5. 通过 `npm run dev` 启动后端
### 前端
后端使用 [Next.js](https://nextjs.org) 建立,使用 Typescript
#### 搭建
1. 首先启动后端
2. 打开 `frontend` 文件夹
3. 通过 `npm install` 安装依赖
4. 通过 `npm run dev` 启动前端
开发项目配置完成
### 测试
目前阶段我们只有后端的系统测试,在 `backend` 文件夹运行 `npm run test:system` 来执行系统测试

View File

@@ -1,129 +0,0 @@
# <div align="center"><img src="https://user-images.githubusercontent.com/58886915/166198400-c2134044-1198-4647-a8b6-da9c4a204c68.svg" width="40"/> </br>Pingvin Share</div>
---
_Leer esto en otro idioma: [Inglés](/README.md), [Español](/docs/README.es.md), [Chino Simplificado](/docs/README.zh-cn.md), [日本語](/docs/README.ja-jp.md)_
---
Pingvin Share es una plataforma de intercambio de archivos autoalojada y una alternativa a WeTransfer.
## ✨ Características
- Compartir archivos utilizando un enlace
- Tamaño de archivo ilimitado (unicamente restringido por el espacio en disco)
- Establecer una fecha de caducidad para los recursos compartidos
- Uso compartido seguro con límites de visitantes y contraseñas
- Destinatarios de correo electrónico
- Integración con ClamAV para escaneos de seguridad
## 🐧 Conoce Pingvin Share
- [Demo](https://pingvin-share.dev.eliasschneider.com)
- [Reseña realizada por No Solo Hacking (español)](https://www.youtube.com/watch?v=ocd4EpLTYkU)
- [Reseña por DB Tech (inglés)](https://www.youtube.com/watch?v=rWwNeZCOPJA)
<img src="https://user-images.githubusercontent.com/58886915/225038319-b2ef742c-3a74-4eb6-9689-4207a36842a4.png" width="700"/>
## ⌨️ Instalación
> Nota: Pingvin Share está en sus primeras etapas y puede contener errores.
### Instalación con Docker (recomendada)
1. Descarge el archivo `docker-compose.yml`
2. Ejecute `docker-compose up -d`
El sitio web ahora está esperando conexiones en `http://localhost:3000`, ¡diviértase usando Pingvin Share 🐧!
### Instalación autónoma
Herramientas requeridas:
- [Node.js](https://nodejs.org/en/download/) >= 16
- [Git](https://git-scm.com/downloads)
- [pm2](https://pm2.keymetrics.io/) para ejecutar Pingvin Share en segundo plano
```bash
git clone https://github.com/stonith404/pingvin-share
cd pingvin-share
# Consultar la última versión
git fetch --tags && git checkout $(git describe --tags `git rev-list --tags --max-count=1`)
# Iniciar el backend
cd backend
npm install
npm run build
pm2 start --name="pingvin-share-backend" npm -- run prod
# Iniciar el frontend
cd ../frontend
npm install
npm run build
pm2 start --name="pingvin-share-frontend" npm -- run start
```
El sitio web ahora está esperando conexiones en `http://localhost:3000`, ¡diviértase usando Pingvin Share 🐧!
### Integraciones
#### ClamAV (Unicamente con Docker)
ClamAV se utiliza para escanear los recursos compartidos en busca de archivos maliciosos y eliminarlos si los encuentra.
1. Añade el contenedor ClamAV al stack de Docker Compose (ver `docker-compose.yml`) e inicie el contenedor.
2. Docker esperará a que ClamAV se inicie antes de iniciar Pingvin Share. Esto puede tardar uno o dos minutos.
3. Los registros de Pingvin Share ahora deberían decir "ClamAV está activo".
Por favor, ten en cuenta que ClamAV necesita muchos [recursos](https://docs.clamav.net/manual/Installing/Docker.html#memory-ram-requirements).
### Recursos adicionales
- [Instalación en Synology NAS (Inglés)](https://mariushosting.com/how-to-install-pingvin-share-on-your-synology-nas/)
### Actualizar a una nueva versión
Dado que Pingvin Share se encuentra en una fase inicial, consulte las notas de la versión para conocer los cambios de última hora antes de actualizar.
#### Docker
```bash
docker compose pull
docker compose up -d
```
#### Instalación autónoma
1. Deten la aplicación en ejecución
```bash
pm2 stop pingvin-share-backend pingvin-share-frontend
```
2. Repite los pasos de la [guía de instalación](#instalación-autonoma) excepto el paso de `git clone`.
```bash
cd pingvin-share
# Consultar la última versión
git fetch --tags && git checkout $(git describe --tags `git rev-list --tags --max-count=1`)
# Iniciar el backend
cd backend
npm run build
pm2 restart pingvin-share-backend
# Iniciar frontend
cd ../frontend
npm run build
pm2 restart pingvin-share-frontend
```
### Marca personalizada
Puedes cambiar el nombre y el logotipo de la aplicación visitando la página de configuración de administrador.
## 🖤 Contribuye
¡Eres bienvenido a contribuir a Pingvin Share! Sige la [guía de contribución](/CONTRIBUTING.md) para empezar.

View File

@@ -1,158 +0,0 @@
# <div align="center"><img src="https://user-images.githubusercontent.com/58886915/166198400-c2134044-1198-4647-a8b6-da9c4a204c68.svg" width="40"/> </br>Pingvin Share</div>
---
_READMEを別の言語で読む: [Spanish](/docs/README.es.md), [English](/README.md), [Simplified Chinese](/docs/README.zh-cn.md), [日本語](/docs/README.ja-jp.md)_
---
Pingvin Share は、セルフホスト型のファイル共有プラットフォームであり、WeTransfer、ギガファイル便などの代替プラットフォームです。
## ✨ 特徴的な機能
- リンクを用いたファイル共有
- ファイルサイズ無制限 (ストレージスペースの範囲内で)
- 共有への有効期限の設定
- 訪問回数の制限とパスワードの設定により共有を安全に保つ
- メールでリンクを共有
- ClamAVと連携して、ウイルスチェックが可能
## 🐧 Pingvin Shareについて知る
- [デモ](https://pingvin-share.dev.eliasschneider.com)
- [DB Techによるレビュー](https://www.youtube.com/watch?v=rWwNeZCOPJA)
<img src="https://user-images.githubusercontent.com/58886915/225038319-b2ef742c-3a74-4eb6-9689-4207a36842a4.png" width="700"/>
## ⌨️ セットアップ
> 注意: Pingvin Shareは、早期段階であり、バグが含まれている場合があります。
### Dockerでインストール (おすすめ)
1. `docker-compose.yml`ファイルをダウンロード
2. `docker-compose up -d`を実行
Webサイトは、`http://localhost:3000`でリッスンされます。これでPingvin Shareをお使い頂けます🐧!
### スタンドアローンインストール
必要なツール:
- [Node.js](https://nodejs.org/en/download/) >= 16
- [Git](https://git-scm.com/downloads)
- [pm2](https://pm2.keymetrics.io/) Pingvin Shareをバックグラウンドで動作させるために必要
```bash
git clone https://github.com/stonith404/pingvin-share
cd pingvin-share
# 最新バージョンをチェックアウト
git fetch --tags && git checkout $(git describe --tags `git rev-list --tags --max-count=1`)
# バックエンドを開始
cd backend
npm install
npm run build
pm2 start --name="pingvin-share-backend" npm -- run prod
#フロントエンドを開始
cd ../frontend
npm install
npm run build
pm2 start --name="pingvin-share-frontend" npm -- run start
```
Webサイトは、`http://localhost:3000`でリッスンされます。これでPingvin Shareをお使い頂けます🐧!
### 連携機能
#### ClamAV (Dockerのみ)
ClamAVは、共有されたファイルをスキャンし、感染したファイルを見つけた場合に削除するために使用されます。
1. ClamAVコンテナをDocker Composeの定義ファイル(`docker-compose.yml`を確認)に追加し、コンテナを開始してください。
2. Dockerは、Pingvin Shareを開始する前に、ClamAVの準備が整うまで待機します。これには、1分から2分ほどかかります。
3. Pingvin Shareのログに"ClamAV is active"というログが記録されます。
ClamAVは、非常に多くのリソースを必要とします、詳しくは[リソース](https://docs.clamav.net/manual/Installing/Docker.html#memory-ram-requirements)をご確認ください。
### 追加情報
- [Synology NASへのインストール方法](https://mariushosting.com/how-to-install-pingvin-share-on-your-synology-nas/)
### 新しいバージョンへのアップグレード
Pingvin Shareは早期段階のため、アップグレード前に必ずリリースートを確認して、アップグレードしても問題ないかどうかご確認ください。
#### Docker
```bash
docker compose pull
docker compose up -d
```
#### スタンドアローン
1. アプリを停止する
```bash
pm2 stop pingvin-share-backend pingvin-share-frontend
```
2. `git clone`のステップを除いて、[インストールガイド](#stand-alone-installation)をくり返してください。
```bash
cd pingvin-share
# 最新バージョンをチェックアウト
git fetch --tags && git checkout $(git describe --tags `git rev-list --tags --max-count=1`)
# バックエンドを開始
cd backend
npm run build
pm2 restart pingvin-share-backend
#フロントエンドを開始
cd ../frontend
npm run build
pm2 restart pingvin-share-frontend
```
### 設定
管理者のダッシュボード内の「設定」ページから、Pingvin Shareをカスタマイズできます。
#### 環境変数
インストール時の特定の設定で、環境変数を使用できます。次の環境変数が使用可能です:
##### バックエンド
| 変数名 | デフォルト値 | 説明 |
| ---------------- | -------------------------------------------------- | -------------------------------------- |
| `PORT` | `8080` | バックエンドがリッスンするポート番号 |
| `DATABASE_URL` | `file:../data/pingvin-share.db?connection_limit=1` | SQLiteのURL |
| `DATA_DIRECTORY` | `./data` | データを保管するディレクトリ |
| `CLAMAV_HOST` | `127.0.0.1` | ClamAVサーバーのIPアドレス |
| `CLAMAV_PORT` | `3310` | ClamAVサーバーのポート番号 |
##### フロントエンド
| 変数名 | デフォルト値 | 説明 |
| --------- | ----------------------- | ---------------------------------------- |
| `PORT` | `3000` | フロントエンドがリッスンするポート番号 |
| `API_URL` | `http://localhost:8080` | フロントエンドからアクセスするバックエンドへのURL |
## 🖤 コントリビュート
### 翻訳
Pingvin Shareをあなたが使用している言語に翻訳するお手伝いを募集しています。
[Crowdin](https://crowdin.com/project/pingvin-share)上で、簡単にPingvin Shareの翻訳作業への参加が可能です。
あなたの言語がありませんか? 気軽に[リクエスト](https://github.com/stonith404/pingvin-share/issues/new?assignees=&labels=language-request&projects=&template=language-request.yml&title=%F0%9F%8C%90+Language+request%3A+%3Clanguage+name+in+english%3E)してください。
翻訳中に問題がありましたか? [ローカライズに関するディスカッション](https://github.com/stonith404/pingvin-share/discussions/198)に是非参加してください。
### プロジェクト
Pingvin Shareへのコントリビュートをいつでもお待ちしています [コントリビューションガイド](/CONTRIBUTING.md)を確認して、是非参加してください。

View File

@@ -1,126 +0,0 @@
# <div align="center"><img src="https://user-images.githubusercontent.com/58886915/166198400-c2134044-1198-4647-a8b6-da9c4a204c68.svg" width="40"/> </br>Pingvin Share</div>
---
_选择合适的语言阅读: [西班牙语](/docs/README.es.md), [英语](/README.md), [简体中文](/docs/README.zh-cn.md), [日本语](/docs/README.ja-jp.md)_
---
Pingvin Share 是一个可自建的文件分享平台,是 WeTransfer 的一个替代品
## ✨ 特性
- 通过可自定义后缀的链接分享文件
- 可自定义任意大小的文件上传限制 (受制于托管所在的硬盘大小)
- 对共享链接设置有效期限
- 对共享链接设置访问次数和访问密码
- 通过邮件自动发送共享链接
- 整合 ClamAV 进行反病毒检查
## 🐧 了解一下 Pingvin Share
- [示例网站](https://pingvin-share.dev.eliasschneider.com)
- [DB Tech 推荐视频](https://www.youtube.com/watch?v=rWwNeZCOPJA)
<img src="https://user-images.githubusercontent.com/58886915/225038319-b2ef742c-3a74-4eb6-9689-4207a36842a4.png" width="700"/>
## ⌨️ 自建指南
> 注意Pingvin Share 仍处于开发阶段并且可能存在 bugs
### Docker 部署 (推荐)
1. 下载 `docker-compose.yml`
2. 运行命令 `docker-compose up -d`
现在网站运行在 `http://localhost:3000`,尝试一下你本地的 Pingvin Share 🐧!
### Stand-alone 部署
必须的依赖:
- [Node.js](https://nodejs.org/en/download/) >= 16
- [Git](https://git-scm.com/downloads)
- [pm2](https://pm2.keymetrics.io/) 用于后台运行 Pingvin Share
```bash
git clone https://github.com/stonith404/pingvin-share
cd pingvin-share
# 获取最新的版本
git fetch --tags && git checkout $(git describe --tags `git rev-list --tags --max-count=1`)
# 启动后端 backend
cd backend
npm install
npm run build
pm2 start --name="pingvin-share-backend" npm -- run prod
# 启动前端 frontend
cd ../frontend
npm install
npm run build
pm2 start --name="pingvin-share-frontend" npm -- run start
```
现在网站运行在 `http://localhost:3000`,尝试一下你本地的 Pingvin Share 🐧!
### 整合组件
#### ClamAV (仅限 Docker 部署)
扫描上传文件中是否存在可疑文件,如果存在 ClamAV 会自动移除
1. 在 docker-compose 配置中添加 ClamAV 容器 (见 `docker-compose.yml` 注释部分) 并启动容器
2. Docker 会在启动 Pingvin Share 前启动 ClamAV也许会花费 1-2 分钟
3. Pingvin Share 日志中应该有 "ClamAV is active"
请注意 ClamAV 会消耗很多 [系统资源(特别是内存)](https://docs.clamav.net/manual/Installing/Docker.html#memory-ram-requirements)
### 更多资源
- [群晖 NAS 配置](https://mariushosting.com/how-to-install-pingvin-share-on-your-synology-nas/)
### 升级
因为 Pingvin Share 仍处在开发阶段,在升级前请务必阅读 release notes 避免不可逆的改变
#### Docker 升级
```bash
docker compose pull
docker compose up -d
```
#### Stand-alone 升级
1. 停止正在运行的 app
```bash
pm2 stop pingvin-share-backend pingvin-share-frontend
```
2. 重复 [installation guide](#stand-alone-installation) 中的步骤,除了 `git clone` 这一步
```bash
cd pingvin-share
# 获取最新的版本
git fetch --tags && git checkout $(git describe --tags `git rev-list --tags --max-count=1`)
# 启动后端 backend
cd backend
npm run build
pm2 restart pingvin-share-backend
# 启动前端 frontend
cd ../frontend
npm run build
pm2 restart pingvin-share-frontend
```
### 自定义品牌
你可以在管理员配置页面改变网站的名字和 logo
## 🖤 提交贡献
非常欢迎向 Pingvin Share 提交贡献! 请阅读 [contribution guide](/CONTRIBUTING.md) 来提交你的贡献

3
docs/babel.config.js Normal file
View File

@@ -0,0 +1,3 @@
module.exports = {
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
};

View File

@@ -0,0 +1,91 @@
# Contributing
We would ❤️ for you to contribute to Pingvin Share and help make it better! All contributions are welcome, including issues, suggestions, pull requests and more.
## Getting started
You've found a bug, have suggestion or something else, just create an issue on GitHub and we can get in touch 😊.
## Submit a Pull Request
Before you submit the pull request for review please ensure that
- The pull request naming follows the [Conventional Commits specification](https://www.conventionalcommits.org):
`<type>[optional scope]: <description>`
example:
```
feat(share): add password protection
```
When `TYPE` can be:
- **feat** - is a new feature
- **doc** - documentation only changes
- **fix** - a bug fix
- **refactor** - code change that neither fixes a bug nor adds a feature
- Your pull request has a detailed description
- You run `npm run format` to format the code
<details>
<summary>Don't know how to create a pull request? Learn how to create a pull request</summary>
1. Create a fork of the repository by clicking on the `Fork` button in the Pingvin Share repository
2. Clone your fork to your machine with `git clone`
```
$ git clone https://github.com/[your_username]/pingvin-share
```
3. Work - commit - repeat
4. Push changes to GitHub
```
$ git push origin [name_of_your_new_branch]
```
5. Submit your changes for review
If you go to your repository on GitHub, you'll see a `Compare & pull request` button. Click on that button.
6. Start a Pull Request
7. Now submit the pull request and click on `Create pull request`.
8. Get a code review approval/reject
</details>
## Setup project
Pingvin Share consists of a frontend and a backend.
### Backend
The backend is built with [Nest.js](https://nestjs.com) and uses Typescript.
#### Setup
1. Open the `backend` folder
2. Install the dependencies with `npm install`
3. Push the database schema to the database by running `npx prisma db push`
4. Seed the database with `npx prisma db seed`
5. Start the backend with `npm run dev`
### Frontend
The frontend is built with [Next.js](https://nextjs.org) and uses Typescript.
#### Setup
1. Start the backend first
2. Open the `frontend` folder
3. Install the dependencies with `npm install`
4. Start the frontend with `npm run dev`
You're all set!
### Testing
At the moment we only have system tests for the backend. To run these tests, run `npm run test:system` in the backend folder.

View File

@@ -0,0 +1,8 @@
# Translating
You can help to translate Pingvin Share into your language.
On [Crowdin](https://crowdin.com/project/pingvin-share) you can easily translate Pingvin Share online.
Is your language not on Crowdin? Feel free to [Request it](https://github.com/stonith404/pingvin-share/issues/new?assignees=&labels=language-request&projects=&template=language-request.yml&title=%F0%9F%8C%90+Language+request%3A+%3Clanguage+name+in+english%3E).
Any issues while translating? Feel free to participate in the [Localization discussion](https://github.com/stonith404/pingvin-share/discussions/198).

24
docs/docs/introduction.md Normal file
View File

@@ -0,0 +1,24 @@
---
id: introduction
---
# Introduction
Pingvin Share is self-hosted file sharing platform and an alternative for WeTransfer.
## Features
- Share files using a link
- Unlimited file size (restricted only by disk space)
- Set an expiration date for shares
- Secure shares with visitor limits and passwords
- Email recipients
- Integration with ClamAV for security scans
And more!
## Get to know Pingvin Share
- [Demo](https://pingvin-share.dev.eliasschneider.com)
- [Review by DB Tech](https://www.youtube.com/watch?v=rWwNeZCOPJA)
<img src="https://user-images.githubusercontent.com/58886915/225038319-b2ef742c-3a74-4eb6-9689-4207a36842a4.png" width="700"/>

View File

@@ -0,0 +1,56 @@
---
id: configuration
---
# Configuration
You can customize Pingvin Share by going to the configuration page in your admin dashboard `/admin/config`.
## General
The **General** Tab will let you customize your Pingvin Share instance to your liking.
### App name
To change the name of your instance, insert any text into `App name`.
### App URL
To make your App available trough your own **domain**, insert your specific domain and also subdomain if needed. Add an `https://` if you have an SSL certificate installed. If this is not the case, use `http://`.
### Show home page
If you don't like the **home page** Pingvin Share provides and you just want the upload tab to be the main page, toggle this to `true`.
### Logo
Not only you can change your instances name, but also the logo it shows everywhere. To do that, upload an image as `png` with a 1:1 aspect ratio.
---
### Environment variables
For installation specific configuration, you can use environment variables. The following variables are available:
#### Backend
| Variable | Default Value | Description |
| ---------------- | -------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `BACKEND_PORT` | `8080` | The port on which the backend listens. |
| `DATABASE_URL` | `file:../data/pingvin-share.db?connection_limit=1` | The URL of the SQLite database. |
| `DATA_DIRECTORY` | `./data` | The directory where data is stored. |
| `CLAMAV_HOST` | `127.0.0.1` or `clamav` when running with Docker | The IP address of the ClamAV server. See the [ClamAV docs](integrations.md#clamav) for more information. |
| `CLAMAV_PORT` | `3310` | The port number of the ClamAV server. |
#### Frontend
| Variable | Default Value | Description |
| --------- | ----------------------- | ---------------------------------------- |
| `PORT` | `3000` | The port on which the frontend listens. |
| `API_URL` | `http://localhost:8080` | The URL of the backend for the frontend. |
#### Reverse Proxy (inside the Docker container)
| Variable | Default Value | Description |
| ------------- | ------------- | ----------------------------------------------------------------------------------------------------------- |
| `TRUST_PROXY` | `false` | Whether Pingvin Share is behind a reverse proxy. If set to `true`, the `X-Forwarded-For` header is trusted. |

View File

@@ -0,0 +1,55 @@
---
id: installation
---
# Installation
### Installation with Docker (recommended)
1. Download the `docker-compose.yml` file
2. Run `docker compose up -d`
The website is now listening on `http://localhost:3000`, have fun with Pingvin Share 🐧!
### Installation with Portainer
1. In the **Stacks** menu, click the **Add stack** button
2. Give you stack a name (ex. pingvinshare)
3. In the web editor, paste the content of the [docker-compose](https://github.com/stonith404/pingvin-share/blob/main/docker-compose.yml) file.
4. Edit the external port and the environment variables (optional).
5. Click on **Deploy the stack**.
Your container is now listening on `http://localhost:<externalport>`, have fun with Pingvin Share 🐧!
### Stand-alone Installation
Required tools:
- [Node.js](https://nodejs.org/en/download/) >= 16
- [Git](https://git-scm.com/downloads)
- [pm2](https://pm2.keymetrics.io/) for running Pingvin Share in the background
```bash
git clone https://github.com/stonith404/pingvin-share
cd pingvin-share
# Checkout the latest version
git fetch --tags && git checkout $(git describe --tags `git rev-list --tags --max-count=1`)
# Start the backend
cd backend
npm install
npm run build
pm2 start --name="pingvin-share-backend" npm -- run prod
# Start the frontend
cd ../frontend
npm install
npm run build
API_URL=http://localhost:8080 # Set the URL of the backend, default: http://localhost:8080
pm2 start npm --name "pingvin-share-frontend" -- run start
```
**Uploading Large Files**: By default, Pingvin Share uses a built-in reverse proxy to reduce the installation steps. However, this reverse proxy is not optimized for uploading large files. If you wish to upload larger files, you can either use the Docker installation or set up your own reverse proxy. An example configuration for Caddy can be found in `./reverse-proxy/Caddyfile`.
The website is now listening on `http://localhost:3000`, have fun with Pingvin Share 🐧!

View File

@@ -0,0 +1,42 @@
---
id: integrations
---
# Integrations
## ClamAV
ClamAV is used to scan shares for malicious files and remove them if found.
Please note that ClamAV needs a lot of [ressources](https://docs.clamav.net/manual/Installing/Docker.html#memory-ram-requirements).
### Docker
If you are already running ClamAV elsewhere, you can specify the `CLAMAV_HOST` environment variable to point to that instance.
Else you have to add the ClamAV container to the Pingvin Share Docker Compose stack:
1. Add the ClamAV container to the Docker Compose stack and start the container.
```diff
services:
pingvin-share:
image: stonith404/pingvin-share
...
+ depends_on:
+ clamav:
+ condition: service_healthy
+ clamav:
+ restart: unless-stopped
+ image: clamav/clamav
```
2. Docker will wait for ClamAV to start before starting Pingvin Share. This may take a minute or two.
3. The Pingvin Share logs should now log "ClamAV is active"
### Stand-Alone
1. Install ClamAV
2. Specify the `CLAMAV_HOST` environment variable for the backend and restart the Pingvin Share backend.

View File

@@ -1,3 +1,7 @@
---
id: oauth2login
---
# OAuth 2 Login Guide
## Config Built-in OAuth 2 Providers
@@ -24,8 +28,7 @@ Redirect URL: `https://<your-domain>/api/oauth/callback/google`
Please follow the [official guide](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app) to register an application.
> [!IMPORTANT]
> **Microsoft Tenant** you set in the admin panel must match the **supported account types** you set in the Microsoft Entra admin center, otherwise the OAuth login will not work. Refer to the [official documentation](https://learn.microsoft.com/en-us/entra/identity-platform/v2-protocols-oidc#find-your-apps-openid-configuration-document-uri) for more details.
> [!IMPORTANT] > **Microsoft Tenant** you set in the admin panel must match the **supported account types** you set in the Microsoft Entra admin center, otherwise the OAuth login will not work. Refer to the [official documentation](https://learn.microsoft.com/en-us/entra/identity-platform/v2-protocols-oidc#find-your-apps-openid-configuration-document-uri) for more details.
Redirect URL: `https://<your-domain>/api/oauth/callback/microsoft`
@@ -37,9 +40,11 @@ Redirect URL: `https://<your-domain>/api/oauth/callback/discord`
### OpenID Connect
Generic OpenID Connect provider is also supported, we have tested it on Keycloak, Authentik and Casdoor.
Generic OpenID Connect provider is also supported, we have tested it on Keycloak, Authentik, Casdoor and [Pocket ID](https://github.com/stonith404/pocket-id).
Redirect URL: `https://<your-domain>/api/oauth/callback/oidc`
Redirect URI: `https://<your-domain>/api/oauth/callback/oidc`
Post Logout Redirect URI: `https://<your-domain>`
## Custom your OAuth 2 Provider
@@ -67,8 +72,8 @@ const configVariables: ConfigVariables = {
defaultValue: "",
obscured: true,
},
}
}
},
};
```
### 2. Create provider class
@@ -106,23 +111,29 @@ Register your provider in [`OAuthModule`](../backend/src/oauth/oauth.module.ts)
// your provider
{
provide: "OAUTH_PROVIDERS",
useFactory(github: GitHubProvider, /* your provider */): Record<string, OAuthProvider<unknown>> {
useFactory(
github: GitHubProvider /* your provider */
): Record<string, OAuthProvider<unknown>> {
return {
github,
/* your provider */
};
},
inject: [GitHubProvider, /* your provider */],
inject: [GitHubProvider /* your provider */],
},
],
})
export class OAuthModule {
}
export class OAuthModule {}
```
```ts
export interface OAuthSignInDto {
provider: 'github' | 'google' | 'microsoft' | 'discord' | 'oidc' /* your provider*/;
provider:
| "github"
| "google"
| "microsoft"
| "discord"
| "oidc" /* your provider*/;
providerId: string;
providerUsername: string;
email: string;
@@ -136,10 +147,10 @@ Add an icon in [`oauth.util.tsx`](../frontend/src/utils/oauth.util.tsx).
```tsx
const getOAuthIcon = (provider: string) => {
return {
'github': <SiGithub />,
github: <SiGithub />,
/* your provider */
}[provider];
}
};
```
### 5. Add i18n text

View File

@@ -0,0 +1,49 @@
---
id: upgrading
---
# Upgrading
### Upgrade to a new version
As Pingvin Share is in early stage, see the release notes for breaking changes before upgrading.
#### Docker
```bash
docker compose pull
docker compose up -d
```
### Portainer
1. In your container page, click on Recreate.
2. Check the Re-Pull image toggle.
3. Click on Recreate.
#### Stand-alone
1. Stop the running app
```bash
pm2 stop pingvin-share-backend pingvin-share-frontend
```
2. Repeat the steps from the [installation guide](#stand-alone-installation) except the `git clone` step.
```bash
cd pingvin-share
# Checkout the latest version
git fetch --tags && git checkout $(git describe --tags `git rev-list --tags --max-count=1`)
# Start the backend
cd backend
npm install
npm run build
pm2 restart pingvin-share-backend
# Start the frontend
cd ../frontend
npm install
npm run build
pm2 restart pingvin-share-frontend
```
Note that environemnt variables are not picked up when using pm2 restart, if you actually want to change configs, you need to run ````pm2 --update-env restart````

64
docs/docusaurus.config.ts Normal file
View File

@@ -0,0 +1,64 @@
import type * as Preset from "@docusaurus/preset-classic";
import type { Config } from "@docusaurus/types";
import { themes as prismThemes } from "prism-react-renderer";
const config: Config = {
title: "Pingvin Share",
tagline:
"Pingvin Share is self-hosted file sharing platform and an alternative for WeTransfer.",
favicon: "img/pingvinshare.svg",
url: "https://stonith404.github.io",
baseUrl: "/pingvin-share/",
organizationName: "stonith404",
projectName: "pingvin-share",
onBrokenLinks: "warn",
onBrokenMarkdownLinks: "warn",
i18n: {
defaultLocale: "en",
locales: ["en"],
},
presets: [
[
"classic",
{
docs: {
routeBasePath: "/",
sidebarPath: "./sidebars.ts",
editUrl: "https://github.com/stonith404/pingvin-share/edit/main/docs",
},
blog: false,
} satisfies Preset.Options,
],
],
themeConfig: {
image: "img/pingvinshare.svg",
colorMode: {
respectPrefersColorScheme: true,
},
navbar: {
title: "Pingvin Share",
logo: {
alt: "Pingvin Share Logo",
src: "img/pingvinshare.svg",
},
items: [
{
href: "https://github.com/stonith404/pingvin-share",
label: "GitHub",
position: "right",
},
],
},
prism: {
theme: prismThemes.github,
darkTheme: prismThemes.dracula,
},
} satisfies Preset.ThemeConfig,
};
export default config;

14671
docs/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

47
docs/package.json Normal file
View File

@@ -0,0 +1,47 @@
{
"name": "pingvindocs",
"version": "0.0.0",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "GIT_USER=stonith404 docusaurus deploy",
"clear": "docusaurus clear",
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids",
"typecheck": "tsc"
},
"dependencies": {
"@docusaurus/core": "3.5.2",
"@docusaurus/preset-classic": "3.5.2",
"@mdx-js/react": "^3.0.1",
"clsx": "^2.1.1",
"prism-react-renderer": "^2.4.0",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "3.5.2",
"@docusaurus/tsconfig": "3.5.2",
"@docusaurus/types": "3.5.2",
"typescript": "~5.6.2"
},
"browserslist": {
"production": [
">0.5%",
"not dead",
"not op_mini all"
],
"development": [
"last 3 chrome version",
"last 3 firefox version",
"last 5 safari version"
]
},
"engines": {
"node": ">=18.0"
}
}

72
docs/sidebars.ts Normal file
View File

@@ -0,0 +1,72 @@
import type { SidebarsConfig } from "@docusaurus/plugin-content-docs";
/**
* Creating a sidebar enables you to:
- create an ordered group of docs
- render a sidebar for each doc of that group
- provide next/previous navigation
The sidebars can be generated from the filesystem, or explicitly defined here.
Create as many sidebars as you want.
*/
const sidebars: SidebarsConfig = {
docsSidebar: [
{
type: "doc",
id : "introduction",
},
{
type: "category",
label: "Getting Started",
items: [
{
type: "doc",
id: "setup/installation",
},
{
type: "doc",
id: "setup/configuration",
},
{
type: "doc",
id: "setup/integrations",
},
{
type: "doc",
id: "setup/oauth2login",
},
{
type: "doc",
id: "setup/upgrading",
},
],
},
{
type: "category",
label: "Helping Out",
items: [
{
type: "doc",
id: "help-out/translate",
},
{
type: "doc",
id: "help-out/contribute",
},
],
},
{
type: "link",
label: "Demo",
href: "https://pingvin-share.dev.eliasschneider.com",
},
{
type: "link",
label: "Discord",
href: "https://discord.gg/HutpbfB59Q",
},
],
};
export default sidebars;

6
docs/src/pages/index.tsx Normal file
View File

@@ -0,0 +1,6 @@
import React from 'react';
import { Redirect } from 'react-router-dom';
export default function Home() {
return <Redirect to='/pingvin-share/introduction' />;
}

BIN
docs/static/img/image.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

1
docs/static/img/pingvinshare.svg vendored Normal file
View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 943.11 911.62"><ellipse cx="471.56" cy="454.28" rx="471.56" ry="454.28" fill="#46509e"/><ellipse cx="471.56" cy="390.28" rx="233.66" ry="207" fill="#37474f"/><path d="M705.22,849c-36.69,21.14-123.09,64.32-240.64,62.57A469.81,469.81,0,0,1,237.89,849V394.76H705.22Z" fill="#37474f"/><path d="M658.81,397.7V873.49a478.12,478.12,0,0,1-374.19,0V397.7c0-95.55,83.78-173,187.1-173S658.81,302.15,658.81,397.7Z" fill="#fff"/><polygon points="565.02 431.68 471.56 514.49 378.09 431.68 565.02 431.68" fill="#46509e"/><ellipse cx="378.09" cy="369.58" rx="23.37" ry="20.7" fill="#37474f"/><ellipse cx="565.02" cy="369.58" rx="23.37" ry="20.7" fill="#37474f"/><path d="M658.49,400.63c0-40-36.6-72.45-81.79-72.45s-81.78,32.41-81.78,72.45a64.79,64.79,0,0,0,7.9,31.05H440.29a64.79,64.79,0,0,0,7.9-31.05c0-40-36.59-72.45-81.78-72.45s-81.79,32.41-81.79,72.45l-46.73-10.35c0-114.31,104.64-207,233.67-207s233.66,92.69,233.66,207Z" fill="#37474f"/></svg>

After

Width:  |  Height:  |  Size: 1018 B

7
docs/tsconfig.json Normal file
View File

@@ -0,0 +1,7 @@
{
// This file is not used in compilation. It is here just for a nice editor experience.
"extends": "@docusaurus/tsconfig",
"compilerOptions": {
"baseUrl": "."
}
}

View File

@@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.

View File

@@ -18,7 +18,4 @@ module.exports = withPWA({
output: "standalone", env: {
VERSION: version,
},
serverRuntimeConfig: {
apiURL: process.env.API_URL ?? 'http://localhost:8080',
},
});

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "pingvin-share-frontend",
"version": "0.26.0",
"version": "1.6.1",
"scripts": {
"dev": "next dev",
"build": "next build",
@@ -9,7 +9,7 @@
"format": "prettier --end-of-line=auto --write \"src/**/*.ts*\""
},
"dependencies": {
"@emotion/react": "^11.11.4",
"@emotion/react": "^11.13.3",
"@emotion/server": "^11.11.0",
"@mantine/core": "^6.0.21",
"@mantine/dropzone": "^6.0.21",
@@ -18,38 +18,37 @@
"@mantine/modals": "^6.0.21",
"@mantine/next": "^6.0.21",
"@mantine/notifications": "^6.0.21",
"axios": "^1.7.2",
"cookies-next": "^2.1.2",
"axios": "^1.7.7",
"cookies-next": "^4.2.1",
"file-saver": "^2.0.5",
"jose": "^4.15.5",
"jwt-decode": "^3.1.2",
"markdown-to-jsx": "^7.4.7",
"jose": "^5.9.2",
"jwt-decode": "^4.0.0",
"markdown-to-jsx": "^7.5.0",
"mime-types": "^2.1.35",
"moment": "^2.30.1",
"next": "^14.2.3",
"next-cookies": "^2.0.3",
"next": "^14.2.12",
"next-http-proxy-middleware": "^1.2.6",
"next-pwa": "^5.6.0",
"p-limit": "^4.0.0",
"p-limit": "^6.1.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-icons": "^4.12.0",
"react-icons": "^5.3.0",
"react-intl": "^6.6.8",
"sharp": "^0.33.4",
"sharp": "^0.33.5",
"yup": "^1.4.0"
},
"devDependencies": {
"@types/mime-types": "^2.1.4",
"@types/node": "20.12.12",
"@types/react": "18.3.2",
"@types/node": "22.5.5",
"@types/react": "18.3.7",
"@types/react-dom": "18.3.0",
"@typescript-eslint/parser": "^7.10.0",
"axios": "^1.7.2",
"@typescript-eslint/parser": "^8.6.0",
"axios": "^1.7.7",
"eslint": "8.57.0",
"eslint-config-next": "^13.5.6",
"eslint-config-prettier": "^8.10.0",
"prettier": "^3.2.5",
"tar": "^6.2.1",
"typescript": "^5.4.5"
"eslint-config-next": "^14.2.12",
"eslint-config-prettier": "^9.1.0",
"prettier": "^3.3.3",
"tar": "^7.4.3",
"typescript": "^5.6.2"
}
}

View File

@@ -5,10 +5,9 @@ import { translateOutsideContext } from "../../hooks/useTranslate.hook";
const showReverseShareLinkModal = (
modals: ModalsContextProps,
reverseShareToken: string,
appUrl: string,
) => {
const t = translateOutsideContext();
const link = `${appUrl}/upload/${reverseShareToken}`;
const link = `${window.location.origin}/upload/${reverseShareToken}`;
return modals.openModal({
title: t("account.reverseShares.modal.reverse-share-link"),
children: (

View File

@@ -11,11 +11,10 @@ import CopyTextField from "../upload/CopyTextField";
const showShareInformationsModal = (
modals: ModalsContextProps,
share: MyShare,
appUrl: string,
maxShareSize: number,
) => {
const t = translateOutsideContext();
const link = `${appUrl}/s/${share.id}`;
const link = `${window.location.origin}/s/${share.id}`;
const formattedShareSize = byteToHumanSizeString(share.size);
const formattedMaxShareSize = byteToHumanSizeString(maxShareSize);

View File

@@ -2,13 +2,9 @@ import { Stack, TextInput } from "@mantine/core";
import { ModalsContextProps } from "@mantine/modals/lib/context";
import { translateOutsideContext } from "../../hooks/useTranslate.hook";
const showShareLinkModal = (
modals: ModalsContextProps,
shareId: string,
appUrl: string,
) => {
const showShareLinkModal = (modals: ModalsContextProps, shareId: string) => {
const t = translateOutsideContext();
const link = `${appUrl}/s/${shareId}`;
const link = `${window.location.origin}/s/${shareId}`;
return modals.openModal({
title: t("account.shares.modal.share-link"),
children: (

View File

@@ -11,15 +11,24 @@ import {
} from "@mantine/core";
import Link from "next/link";
import { Dispatch, SetStateAction } from "react";
import { TbAt, TbMail, TbShare, TbSocial, TbSquare } from "react-icons/tb";
import {
TbAt,
TbMail,
TbShare,
TbSocial,
TbSquare,
TbBinaryTree,
TbSettings,
} from "react-icons/tb";
import { FormattedMessage } from "react-intl";
const categories = [
{ name: "General", icon: <TbSquare /> },
{ name: "General", icon: <TbSettings /> },
{ name: "Email", icon: <TbMail /> },
{ name: "Share", icon: <TbShare /> },
{ name: "SMTP", icon: <TbAt /> },
{ name: "OAuth", icon: <TbSocial /> },
{ name: "LDAP", icon: <TbBinaryTree /> },
];
const useStyles = createStyles((theme) => ({

View File

@@ -89,15 +89,11 @@ const ManageShareTable = ({
onClick={() => {
if (window.isSecureContext) {
clipboard.copy(
`${config.get("general.appUrl")}/s/${share.id}`,
`${window.location.origin}/s/${share.id}`,
);
toast.success(t("common.notify.copied"));
} else {
showShareLinkModal(
modals,
share.id,
config.get("general.appUrl"),
);
showShareLinkModal(modals, share.id);
}
}}
>

View File

@@ -1,4 +1,4 @@
import { ActionIcon, Box, Group, Skeleton, Table } from "@mantine/core";
import { ActionIcon, Badge, Box, Group, Skeleton, Table } from "@mantine/core";
import { useModals } from "@mantine/modals";
import { TbCheck, TbEdit, TbTrash } from "react-icons/tb";
import User from "../../../types/user.type";
@@ -40,21 +40,28 @@ const ManageUserTable = ({
? skeletonRows
: users.map((user) => (
<tr key={user.id}>
<td>{user.username}</td>
<td>
{user.username}{" "}
{user.isLdap ? (
<Badge style={{ marginLeft: "1em" }}>LDAP</Badge>
) : null}
</td>
<td>{user.email}</td>
<td>{user.isAdmin && <TbCheck />}</td>
<td>
<Group position="right">
<ActionIcon
variant="light"
color="primary"
size="sm"
onClick={() =>
showUpdateUserModal(modals, user, getUsers)
}
>
<TbEdit />
</ActionIcon>
{user.isLdap ? null : (
<ActionIcon
variant="light"
color="primary"
size="sm"
onClick={() =>
showUpdateUserModal(modals, user, getUsers)
}
>
<TbEdit />
</ActionIcon>
)}
<ActionIcon
variant="light"
color="red"

View File

@@ -4,6 +4,7 @@ import {
Container,
createStyles,
Group,
Loader,
Paper,
PasswordInput,
Stack,
@@ -15,7 +16,7 @@ import { useForm, yupResolver } from "@mantine/form";
import { showNotification } from "@mantine/notifications";
import Link from "next/link";
import { useRouter } from "next/router";
import React from "react";
import { useEffect, useState } from "react";
import { TbInfoCircle } from "react-icons/tb";
import { FormattedMessage } from "react-intl";
import * as yup from "yup";
@@ -24,10 +25,23 @@ import useUser from "../../hooks/user.hook";
import useTranslate from "../../hooks/useTranslate.hook";
import authService from "../../services/auth.service";
import { getOAuthIcon, getOAuthUrl } from "../../utils/oauth.util";
import toast from "../../utils/toast.util";
import { safeRedirectPath } from "../../utils/router.util";
import toast from "../../utils/toast.util";
const useStyles = createStyles((theme) => ({
signInWith: {
fontWeight: 500,
"&:before": {
content: "''",
flex: 1,
display: "block",
},
"&:after": {
content: "''",
flex: 1,
display: "block",
},
},
or: {
"&:before": {
content: "''",
@@ -61,14 +75,13 @@ const SignInForm = ({ redirectPath }: { redirectPath: string }) => {
const { refreshUser } = useUser();
const { classes } = useStyles();
const [oauth, setOAuth] = React.useState<string[]>([]);
const [oauthProviders, setOauthProviders] = useState<string[] | null>(null);
const [isRedirectingToOauthProvider, setIsRedirectingToOauthProvider] =
useState(false);
const validationSchema = yup.object().shape({
emailOrUsername: yup.string().required(t("common.error.field-required")),
password: yup
.string()
.min(8, t("common.error.too-short", { length: 8 }))
.required(t("common.error.field-required")),
password: yup.string().required(t("common.error.field-required")),
});
const form = useForm({
@@ -81,7 +94,7 @@ const SignInForm = ({ redirectPath }: { redirectPath: string }) => {
const signIn = async (email: string, password: string) => {
await authService
.signIn(email, password)
.signIn(email.trim(), password.trim())
.then(async (response) => {
if (response.data["loginToken"]) {
// Prompt the user to enter their totp code
@@ -105,15 +118,34 @@ const SignInForm = ({ redirectPath }: { redirectPath: string }) => {
.catch(toast.axiosError);
};
const getAvailableOAuth = async () => {
const oauth = await authService.getAvailableOAuth();
setOAuth(oauth.data);
};
React.useEffect(() => {
getAvailableOAuth().catch(toast.axiosError);
useEffect(() => {
authService
.getAvailableOAuth()
.then((providers) => {
setOauthProviders(providers.data);
if (
providers.data.length === 1 &&
config.get("oauth.disablePassword")
) {
setIsRedirectingToOauthProvider(true);
router.push(getOAuthUrl(window.location.origin, providers.data[0]));
}
})
.catch(toast.axiosError);
}, []);
if (!oauthProviders) return null;
if (isRedirectingToOauthProvider)
return (
<Group align="center" position="center">
<Loader size="sm" />
<Text align="center">
<FormattedMessage id="common.text.redirecting" />
</Text>
</Group>
);
return (
<Container size={420} my={40}>
<Title order={2} align="center" weight={900}>
@@ -128,49 +160,58 @@ const SignInForm = ({ redirectPath }: { redirectPath: string }) => {
</Text>
)}
<Paper withBorder shadow="md" p={30} mt={30} radius="md">
<form
onSubmit={form.onSubmit((values) => {
signIn(values.emailOrUsername, values.password);
})}
>
<TextInput
label={t("signin.input.email-or-username")}
placeholder={t("signin.input.email-or-username.placeholder")}
{...form.getInputProps("emailOrUsername")}
/>
<PasswordInput
label={t("signin.input.password")}
placeholder={t("signin.input.password.placeholder")}
mt="md"
{...form.getInputProps("password")}
/>
{config.get("smtp.enabled") && (
<Group position="right" mt="xs">
<Anchor component={Link} href="/auth/resetPassword" size="xs">
<FormattedMessage id="resetPassword.title" />
</Anchor>
</Group>
)}
<Button fullWidth mt="xl" type="submit">
<FormattedMessage id="signin.button.submit" />
</Button>
</form>
{oauth.length > 0 && (
<Stack mt="xl">
<Group align="center" className={classes.or}>
<Text>{t("signIn.oauth.or")}</Text>
</Group>
{config.get("oauth.disablePassword") || (
<form
onSubmit={form.onSubmit((values) => {
signIn(values.emailOrUsername, values.password);
})}
>
<TextInput
label={t("signin.input.email-or-username")}
placeholder={t("signin.input.email-or-username.placeholder")}
{...form.getInputProps("emailOrUsername")}
/>
<PasswordInput
label={t("signin.input.password")}
placeholder={t("signin.input.password.placeholder")}
mt="md"
{...form.getInputProps("password")}
/>
{config.get("smtp.enabled") && (
<Group position="right" mt="xs">
<Anchor component={Link} href="/auth/resetPassword" size="xs">
<FormattedMessage id="resetPassword.title" />
</Anchor>
</Group>
)}
<Button fullWidth mt="xl" type="submit">
<FormattedMessage id="signin.button.submit" />
</Button>
</form>
)}
{oauthProviders.length > 0 && (
<Stack mt={config.get("oauth.disablePassword") ? undefined : "xl"}>
{config.get("oauth.disablePassword") ? (
<Group align="center" className={classes.signInWith}>
<Text>{t("signIn.oauth.signInWith")}</Text>
</Group>
) : (
<Group align="center" className={classes.or}>
<Text>{t("signIn.oauth.or")}</Text>
</Group>
)}
<Group position="center">
{oauth.map((provider) => (
{oauthProviders.map((provider) => (
<Button
key={provider}
component="a"
target="_blank"
title={t(`signIn.oauth.${provider}`)}
href={getOAuthUrl(config.get("general.appUrl"), provider)}
href={getOAuthUrl(window.location.origin, provider)}
variant="light"
fullWidth
>
{getOAuthIcon(provider)}
{"\u2002" + t(`signIn.oauth.${provider}`)}
</Button>
))}
</Group>

View File

@@ -48,7 +48,7 @@ const SignUpForm = () => {
const signUp = async (email: string, username: string, password: string) => {
await authService
.signUp(email, username, password)
.signUp(email.trim(), username.trim(), password.trim())
.then(async () => {
const user = await refreshUser();
if (user?.isAdmin) {

View File

@@ -39,7 +39,7 @@ const FileList = ({
const t = useTranslate();
const [sort, setSort] = useState<TableSort>({
property: undefined,
property: "name",
direction: "desc",
});
@@ -65,7 +65,7 @@ const FileList = ({
};
const copyFileLink = (file: FileMetaData) => {
const link = `${config.get("general.appUrl")}/api/shares/${
const link = `${window.location.origin}/api/shares/${
share.id
}/files/${file.id}`;

View File

@@ -9,11 +9,13 @@ import {
Switch,
Text,
} from "@mantine/core";
import { useForm } from "@mantine/form";
import { useForm, yupResolver } from "@mantine/form";
import { useModals } from "@mantine/modals";
import { ModalsContextProps } from "@mantine/modals/lib/context";
import { getCookie, setCookie } from "cookies-next";
import moment from "moment";
import { FormattedMessage } from "react-intl";
import * as yup from "yup";
import useTranslate, {
translateOutsideContext,
} from "../../../hooks/useTranslate.hook";
@@ -61,10 +63,26 @@ const Body = ({
sendEmailNotification: false,
expiration_num: 1,
expiration_unit: "-days",
simplified: !!(getCookie("reverse-share.simplified") ?? false),
publicAccess: !!(getCookie("reverse-share.public-access") ?? true),
},
validate: yupResolver(
yup.object().shape({
maxUseCount: yup
.number()
.typeError(t("common.error.invalid-number"))
.min(1, t("common.error.number-too-small", { min: 1 }))
.max(1000, t("common.error.number-too-large", { max: 1000 }))
.required(t("common.error.field-required")),
}),
),
});
const onSubmit = form.onSubmit(async (values) => {
// remember simplified and publicAccess in cookies
setCookie("reverse-share.simplified", values.simplified);
setCookie("reverse-share.public-access", values.publicAccess);
const expirationDate = moment().add(
form.values.expiration_num,
form.values.expiration_unit.replace(
@@ -91,6 +109,8 @@ const Body = ({
values.maxShareSize,
values.maxUseCount,
values.sendEmailNotification,
values.simplified,
values.publicAccess,
)
.then(({ link }) => {
modals.closeAll();
@@ -210,7 +230,28 @@ const Body = ({
})}
/>
)}
<Switch
mt="xs"
labelPosition="left"
label={t("account.reverseShares.modal.simplified")}
description={t(
"account.reverseShares.modal.simplified.description",
)}
{...form.getInputProps("simplified", {
type: "checkbox",
})}
/>
<Switch
mt="xs"
labelPosition="left"
label={t("account.reverseShares.modal.public-access")}
description={t(
"account.reverseShares.modal.public-access.description",
)}
{...form.getInputProps("publicAccess", {
type: "checkbox",
})}
/>
<Button mt="md" type="submit">
<FormattedMessage id="common.button.create" />
</Button>

View File

@@ -7,13 +7,12 @@ import { FormattedMessage } from "react-intl";
import useTranslate, {
translateOutsideContext,
} from "../../../hooks/useTranslate.hook";
import { Share } from "../../../types/share.type";
import { CompletedShare } from "../../../types/share.type";
import CopyTextField from "../CopyTextField";
const showCompletedUploadModal = (
modals: ModalsContextProps,
share: Share,
appUrl: string,
share: CompletedShare,
) => {
const t = translateOutsideContext();
return modals.openModal({
@@ -21,20 +20,35 @@ const showCompletedUploadModal = (
withCloseButton: false,
closeOnEscape: false,
title: t("upload.modal.completed.share-ready"),
children: <Body share={share} appUrl={appUrl} />,
children: <Body share={share} />,
});
};
const Body = ({ share, appUrl }: { share: Share; appUrl: string }) => {
const Body = ({ share }: { share: CompletedShare }) => {
const modals = useModals();
const router = useRouter();
const t = useTranslate();
const link = `${appUrl}/s/${share.id}`;
const isReverseShare = !!router.query["reverseShareToken"];
const link = `${window.location.origin}/s/${share.id}`;
return (
<Stack align="stretch">
<CopyTextField link={link} />
{share.notifyReverseShareCreator === true && (
<Text
size="sm"
sx={(theme) => ({
color:
theme.colorScheme === "dark"
? theme.colors.gray[3]
: theme.colors.dark[4],
})}
>
{t("upload.modal.completed.notified-reverse-share-creator")}
</Text>
)}
<Text
size="xs"
sx={(theme) => ({
@@ -52,7 +66,11 @@ const Body = ({ share, appUrl }: { share: Share; appUrl: string }) => {
<Button
onClick={() => {
modals.closeAll();
router.push("/upload");
if (isReverseShare) {
router.reload();
} else {
router.push("/upload");
}
}}
>
<FormattedMessage id="common.button.done" />

View File

@@ -19,7 +19,7 @@ import { useForm, yupResolver } from "@mantine/form";
import { useModals } from "@mantine/modals";
import { ModalsContextProps } from "@mantine/modals/lib/context";
import moment from "moment";
import { useState } from "react";
import React, { useState } from "react";
import { TbAlertCircle } from "react-icons/tb";
import { FormattedMessage } from "react-intl";
import * as yup from "yup";
@@ -30,23 +30,37 @@ import shareService from "../../../services/share.service";
import { FileUpload } from "../../../types/File.type";
import { CreateShare } from "../../../types/share.type";
import { getExpirationPreview } from "../../../utils/date.util";
import React from "react";
import toast from "../../../utils/toast.util";
const showCreateUploadModal = (
modals: ModalsContextProps,
options: {
isUserSignedIn: boolean;
isReverseShare: boolean;
appUrl: string;
allowUnauthenticatedShares: boolean;
enableEmailRecepients: boolean;
maxExpirationInHours: number;
shareIdLength: number;
simplified: boolean;
},
files: FileUpload[],
uploadCallback: (createShare: CreateShare, files: FileUpload[]) => void,
) => {
const t = translateOutsideContext();
if (options.simplified) {
return modals.openModal({
title: t("upload.modal.title"),
children: (
<SimplifiedCreateUploadModalModal
options={options}
files={files}
uploadCallback={uploadCallback}
/>
),
});
}
return modals.openModal({
title: t("upload.modal.title"),
children: (
@@ -59,6 +73,33 @@ const showCreateUploadModal = (
});
};
const generateShareId = (length: number = 16) => {
const chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let result = "";
const randomArray = new Uint8Array(length >= 3 ? length : 3);
crypto.getRandomValues(randomArray);
randomArray.forEach((number) => {
result += chars[number % chars.length];
});
return result;
};
const generateAvailableLink = async (
shareIdLength: number,
times: number = 10,
): Promise<string> => {
if (times <= 0) {
throw new Error("Could not generate available link");
}
const _link = generateShareId(shareIdLength);
if (!(await shareService.isShareIdAvailable(_link))) {
return await generateAvailableLink(shareIdLength, times - 1);
} else {
return _link;
}
};
const CreateUploadModalBody = ({
uploadCallback,
files,
@@ -69,18 +110,16 @@ const CreateUploadModalBody = ({
options: {
isUserSignedIn: boolean;
isReverseShare: boolean;
appUrl: string;
allowUnauthenticatedShares: boolean;
enableEmailRecepients: boolean;
maxExpirationInHours: number;
shareIdLength: number;
};
}) => {
const modals = useModals();
const t = useTranslate();
const generatedLink = Buffer.from(Math.random().toString(), "utf8")
.toString("base64")
.substr(10, 7);
const generatedLink = generateShareId(options.shareIdLength);
const [showNotSignedInAlert, setShowNotSignedInAlert] = useState(true);
@@ -205,9 +244,7 @@ const CreateUploadModalBody = ({
onClick={() =>
form.setFieldValue(
"link",
Buffer.from(Math.random().toString(), "utf8")
.toString("base64")
.substr(10, 7),
generateShareId(options.shareIdLength),
)
}
>
@@ -216,13 +253,14 @@ const CreateUploadModalBody = ({
</Group>
<Text
truncate
italic
size="xs"
sx={(theme) => ({
color: theme.colors.gray[6],
})}
>
{`${options.appUrl}/s/${form.values.link}`}
{`${window.location.origin}/s/${form.values.link}`}
</Text>
{!options.isReverseShare && (
<>
@@ -347,9 +385,8 @@ const CreateUploadModalBody = ({
placeholder={t("upload.modal.accordion.email.placeholder")}
searchable
creatable
id="recipient_email"
autoComplete="email"
type="email"
id="recipient-emails"
inputMode="email"
getCreateLabel={(query) => `+ ${query}`}
onCreate={(query) => {
if (!query.match(/^\S+@\S+\.\S+$/)) {
@@ -403,7 +440,7 @@ const CreateUploadModalBody = ({
"upload.modal.accordion.security.password.placeholder",
)}
label={t("upload.modal.accordion.security.password.label")}
autoComplete="off"
autoComplete="new-password"
{...form.getInputProps("password")}
/>
<NumberInput
@@ -429,4 +466,110 @@ const CreateUploadModalBody = ({
);
};
const SimplifiedCreateUploadModalModal = ({
uploadCallback,
files,
options,
}: {
files: FileUpload[];
uploadCallback: (createShare: CreateShare, files: FileUpload[]) => void;
options: {
isUserSignedIn: boolean;
isReverseShare: boolean;
allowUnauthenticatedShares: boolean;
enableEmailRecepients: boolean;
maxExpirationInHours: number;
shareIdLength: number;
};
}) => {
const modals = useModals();
const t = useTranslate();
const [showNotSignedInAlert, setShowNotSignedInAlert] = useState(true);
const validationSchema = yup.object().shape({
name: yup
.string()
.transform((value) => value || undefined)
.min(3, t("common.error.too-short", { length: 3 }))
.max(30, t("common.error.too-long", { length: 30 })),
});
const form = useForm({
initialValues: {
name: undefined,
description: undefined,
},
validate: yupResolver(validationSchema),
});
const onSubmit = form.onSubmit(async (values) => {
const link = await generateAvailableLink(options.shareIdLength).catch(
() => {
toast.error(t("upload.modal.link.error.taken"));
return undefined;
},
);
if (!link) {
return;
}
uploadCallback(
{
id: link,
name: values.name,
expiration: "never",
recipients: [],
description: values.description,
security: {
password: undefined,
maxViews: undefined,
},
},
files,
);
modals.closeAll();
});
return (
<Stack>
{showNotSignedInAlert && !options.isUserSignedIn && (
<Alert
withCloseButton
onClose={() => setShowNotSignedInAlert(false)}
icon={<TbAlertCircle size={16} />}
title={t("upload.modal.not-signed-in")}
color="yellow"
>
<FormattedMessage id="upload.modal.not-signed-in-description" />
</Alert>
)}
<form onSubmit={onSubmit}>
<Stack align="stretch">
<Stack align="stretch">
<TextInput
variant="filled"
placeholder={t(
"upload.modal.accordion.name-and-description.name.placeholder",
)}
{...form.getInputProps("name")}
/>
<Textarea
variant="filled"
placeholder={t(
"upload.modal.accordion.name-and-description.description.placeholder",
)}
{...form.getInputProps("description")}
/>
</Stack>
<Button type="submit" data-autofocus>
<FormattedMessage id="common.button.share" />
</Button>
</Stack>
</form>
</Stack>
);
};
export default showCreateUploadModal;

View File

@@ -0,0 +1,42 @@
import { useRouter } from "next/router";
import { useEffect } from "react";
const useConfirmLeave = ({
message,
enabled,
}: {
message: string;
enabled: boolean;
}) => {
const router = useRouter();
useEffect(() => {
if (!enabled) return;
// Show confirmation dialog when route changes
const handleRouteChange = () => {
const confirmLeave = window.confirm(message);
if (!confirmLeave) {
router.events.emit("routeChangeError");
throw "Route change aborted.";
}
};
// Show confirmation when the user tries to leave or reload the page
const handleBeforeUnload = (event: BeforeUnloadEvent) => {
event.preventDefault();
event.returnValue = message;
return message;
};
router.events.on("routeChangeStart", handleRouteChange);
window.addEventListener("beforeunload", handleBeforeUnload);
return () => {
router.events.off("routeChangeStart", handleRouteChange);
window.removeEventListener("beforeunload", handleBeforeUnload);
};
}, [router, message, enabled]);
};
export default useConfirmLeave;

View File

@@ -15,13 +15,17 @@ import polish from "./translations/pl-PL";
import portuguese from "./translations/pt-BR";
import russian from "./translations/ru-RU";
import slovenian from "./translations/sl-SI";
import serbian from "./translations/sr-SP";
import serbianCyrillic from "./translations/sr-SP";
import swedish from "./translations/sv-SE";
import thai from "./translations/th-TH";
import ukrainian from "./translations/uk-UA";
import chineseSimplified from "./translations/zh-CN";
import chineseTraditional from "./translations/zh-TW";
import turkish from "./translations/tr-TR";
import czech from "./translations/cs-CZ";
import viatnamese from "./translations/vi-VN";
import bulgarian from "./translations/bg-BG";
import serbianLatin from "./translations/sr-CS";
export const LOCALES = {
ENGLISH: {
@@ -85,9 +89,14 @@ export const LOCALES = {
messages: thai,
},
SERBIAN: {
name: "Srpski",
name: "Српски",
code: "sr-SP",
messages: serbian,
messages: serbianCyrillic,
},
SERBIAN_LATIN: {
name: "Srpski",
code: "sr-CS",
messages: serbianLatin,
},
DUTCH: {
name: "Nederlands",
@@ -144,4 +153,19 @@ export const LOCALES = {
code: "tr-TR",
messages: turkish,
},
CZECH: {
name: "Čeština",
code: "cs-CZ",
messages: czech,
},
VIATNAMESE: {
name: "Tiếng Việt",
code: "vi-VN",
messages: viatnamese,
},
BULGARIAN: {
name: "Български",
code: "bg-BG",
messages: bulgarian,
},
};

View File

@@ -3,7 +3,7 @@ export default {
"navbar.upload": "رفع",
"navbar.signin": "تسجيل الدخول",
"navbar.home": "الصفحة الرئيسية",
"navbar.signup": "إنشاء حساب",
"navbar.signup": "Sign up",
"navbar.links.shares": "مشاركاتي",
"navbar.links.reverse": "مشاركاتي العكسية",
"navbar.avatar.account": "حسابي",
@@ -16,9 +16,9 @@ export default {
"home.bullet.a.name": "استضافة ذاتية",
"home.bullet.a.description": "قم باستضافة Pingvin Share على جهازك.",
"home.bullet.b.name": "الخصوصية",
"home.bullet.b.description": "ملفاتك تخصّك وحدك فقط، ولا ينبغي أبدًا أن تقع بأيدي طرفٍ ثالث.",
"home.bullet.b.description": "Your files are yours and will never be accessed by third parties.",
"home.bullet.c.name": "ليس هناك أية قيود على حجم الملفات",
"home.bullet.c.description": "ارفع أي ملف تريده مهما كان حجمه كبيرًا. إن مساحة قرصك الصلب هي المحدد الوحيد هنا.",
"home.bullet.c.description": "Upload files as big as you want. Only your hard drive will be your limit.",
"home.button.start": "ابدأ",
"home.button.source": "النص البرمجي المصدري",
// END /
@@ -34,6 +34,7 @@ export default {
"signIn.notify.totp-required.title": "إن المصادقة الثنائية ضرورية",
"signIn.notify.totp-required.description": "فضلًا أدخل رمز المصادقة الثنائية",
"signIn.oauth.or": "أو",
"signIn.oauth.signInWith": "تسجيل الدخول بواسطة تطبيق",
"signIn.oauth.github": "GitHub",
"signIn.oauth.google": "Google",
"signIn.oauth.microsoft": "Microsoft",
@@ -57,12 +58,12 @@ export default {
// /auth/reset-password
"resetPassword.title": "نسيت كلمة سرّك؟",
"resetPassword.description": "اكتب بريدك لتعيد تعيين كلمة السر.",
"resetPassword.notify.success": "إذا كان هذا البريد مسجلًا لدينا فستصله الآن رسالة فيها رابط لإعادة تعيين كلمة السرّ.",
"resetPassword.notify.success": "A message with a link to reset your password has been sent if the provided email exists.",
"resetPassword.button.back": "العودة لصفحة تسجيل الدخول",
"resetPassword.text.resetPassword": "إعادة تعيين كلمة السر",
"resetPassword.text.enterNewPassword": "أدخل كلمة السر الجديدة",
"resetPassword.input.password": "كلمة السر الجديدة",
"resetPassword.notify.passwordReset": "أعدتَ تعيين كلمة السر بنجاح.",
"resetPassword.notify.passwordReset": "Your password has been successfully reset.",
// /account
"account.title": "حسابي",
"account.card.info.title": "معلومات الحساب",
@@ -72,7 +73,7 @@ export default {
"account.card.password.title": "كلمة السر",
"account.card.password.old": "كلمة السر القديمة",
"account.card.password.new": "كلمة السر الجديدة",
"account.card.password.noPasswordSet": "ليس لحسابك كلمة سر. إذا أردت تسجيل الدخول باستخدام البريد وكلمة سر، فعليك أن تُعيِّن كلمة سر.",
"account.card.password.noPasswordSet": "You do not have a password set. To sign in using your email and password, you need to create a password.",
"account.notify.password.success": "غيرت كلمة السر بنجاح",
"account.card.oauth.title": "الدخول بحساب تواصل اجتماعي",
"account.card.oauth.github": "GitHub",
@@ -84,7 +85,7 @@ export default {
"account.card.oauth.unlink": "فك الربط",
"account.card.oauth.unlinked": "تم فك الربط",
"account.modal.unlink.title": "فك ربط الحساب",
"account.modal.unlink.description": "قد يؤدي إلغاء ربط حساباتك الاجتماعية إلى فقدان وصولك لحسابك إذا كنت لا تتذكر اسم المستخدم وكلمة السر الخاصة بك.",
"account.modal.unlink.description": "Unlinking your social accounts may cause you to lose your account if you don't remember your login credentials",
"account.notify.oauth.unlinked.success": "تم فك الربط بنجاح",
"account.card.security.title": "الأمان",
"account.card.security.totp.enable.description": "اكتب كلمة سرّك لبدء تمكين TOTP",
@@ -120,12 +121,12 @@ export default {
"account.shares.table.name": "الاسم",
"account.shares.table.description": "الوصف",
"account.shares.table.visitors": "الزوار",
"account.shares.table.expiresAt": "تاريخ انتهاء الصلاحية",
"account.shares.table.createdAt": "تاريخ الإنشاء",
"account.shares.table.expiresAt": "Expires on",
"account.shares.table.createdAt": "Created on",
"account.shares.table.size": "الحجم",
"account.shares.modal.share-informations": "معلومات المشاركة",
"account.shares.modal.share-link": "رابط المشاركة",
"account.shares.modal.delete.title": "حذف المشاركة {share}",
"account.shares.modal.delete.title": "Delete share: {share}",
"account.shares.modal.delete.description": "هل تريد حذف هذه المشاركة حقاً؟",
// END /account/shares
// /account/reverseShares
@@ -149,8 +150,12 @@ export default {
"account.reverseShares.modal.expiration.year-singular": "سنة",
"account.reverseShares.modal.expiration.year-plural": "سنوات",
"account.reverseShares.modal.max-size.label": "الحد الأقصى لحجم المشاركة",
"account.reverseShares.modal.send-email": "أرسل إشعارًا بالبريد",
"account.reverseShares.modal.send-email.description": "إرسال إشعار بالبريد الإلكتروني عند إنشاء مشاركة باستخدام رابط المشاركة العكسي هذا.",
"account.reverseShares.modal.send-email": "Send email notifications",
"account.reverseShares.modal.send-email.description": "Sends you an email notification when a share is created with this reverse share link.",
"account.reverseShares.modal.simplified": "Simple mode",
"account.reverseShares.modal.simplified.description": "Make it easy for the person uploading the file to share it with you. They will only be able to customize the name and description of the share.",
"account.reverseShares.modal.public-access": "Public access",
"account.reverseShares.modal.public-access.description": "Make the shares created with this reverse share public. If disabled, only you and the share creator will have access to view it.",
"account.reverseShares.modal.max-use.label": "الحد الأقصى لعدد الاستخدامات",
"account.reverseShares.modal.max-use.description": "أقصى عدد من المرّات التي يمكن فيها استخدام هذا الرابط لإنشاء مشاركة.",
"account.reverseShare.never-expires": "لن تنتهي صلاحية هذه المشاركة العكسية أبدًا.",
@@ -178,14 +183,14 @@ export default {
"admin.users.table.username": "اسم المستخدم",
"admin.users.table.email": "البريد",
"admin.users.table.admin": "المدير",
"admin.users.edit.update.title": "تحديث المستخدم {username}",
"admin.users.edit.update.title": "Edit user: {username}",
"admin.users.edit.update.admin-privileges": "صلاحيات المدير",
"admin.users.edit.update.change-password.title": "تغيير كلمة السر",
"admin.users.edit.update.change-password.field": "كلمة السر الجديدة",
"admin.users.edit.update.change-password.button": "حفظ كلمة السر الجديدة",
"admin.users.edit.update.notify.password.success": "غيرت كلمة السر بنجاح",
"admin.users.edit.delete.title": "حذف المستخدم {username}",
"admin.users.edit.delete.description": "هل تريد حقاً حذف هذا المستخدم وكل مشاركاته؟",
"admin.users.edit.delete.title": "Delete user: {username} ?",
"admin.users.edit.delete.description": "Do you really want to delete this user and all their shares?",
// showCreateUserModal.tsx
"admin.users.modal.create.title": "أنشئ مستخدمًا",
"admin.users.modal.create.username": "اسم المستخدم",
@@ -201,17 +206,20 @@ export default {
"admin.shares.table.id": "معرّف المشاركة",
"admin.shares.table.username": "المُنشئ",
"admin.shares.table.visitors": "الزوار",
"admin.shares.table.expires": "تاريخ انتهاء الصلاحية",
"admin.shares.edit.delete.title": "حذف المشاركة {id}",
"admin.shares.table.expires": "Expires on",
"admin.shares.edit.delete.title": "Delete share: {id}",
"admin.shares.edit.delete.description": "هل تريد حذف هذه المشاركة حقاً؟",
// END /admin/shares
// /upload
"upload.title": "رفع",
"upload.notify.confirm-leave": "Are you sure you want to leave this page? Your upload will be canceled.",
"upload.notify.generic-error": "حدث خطأ أثناء إنهاء مشاركتك.",
"upload.notify.count-failed": "فشل رفع {count} ملفات. تجري المحاولة مجددًا.",
"upload.reverse-share.error.invalid.title": "Invalid reverse share link",
"upload.reverse-share.error.invalid.description": "This reverse share has expired or is invalid.",
// Dropzone.tsx
"upload.dropzone.title": "رفع الملفات",
"upload.dropzone.description": "اسحب الملفات إلى هنا لبدء مشاركتك. يمكننا فقط قبول الملفات التي لا يزيد حجمها عن {maxSize} بالمجمل.",
"upload.dropzone.description": "Drag'n'drop files here to start your share. We only accept files up to {maxSize} in total.",
"upload.dropzone.notify.file-too-big": "تتجاوز ملفاتك الحجم الأقصى للمشاركة والذي هو {maxSize}.",
// FileList.tsx
"upload.filelist.name": "الاسم",
@@ -223,8 +231,8 @@ export default {
"upload.modal.not-signed-in": "لم تقم بتسجيل الدخول",
"upload.modal.not-signed-in-description": "لن تتمكن من حذف مشاركتك يدوياً أو عرض عدد الزوار.",
"upload.modal.expires.never": "أبدًا",
"upload.modal.expires.never-long": "لا تنتهي الصلاحية أبداً",
"upload.modal.expires.error.too-long": "انتهاء الصلاحية يتجاوز الحد الأقصى لتاريخ انتهاء الصلاحية والذي هو {max}.",
"upload.modal.expires.never-long": "Permanent share",
"upload.modal.expires.error.too-long": "Expiration date exceeds the maximum of {max}.",
"upload.modal.link.label": "الرابط",
"upload.modal.expires.label": "انتهاء الصلاحية",
"upload.modal.expires.minute-singular": "دقيقة",
@@ -254,6 +262,7 @@ export default {
"upload.modal.completed.never-expires": "لن تنتهي صلاحية هذه المشاركة أبدًا.",
"upload.modal.completed.expires-on": "هذه المشاركة ستنتهي صلاحيتها في {expiration}.",
"upload.modal.completed.share-ready": "المشاركة جاهزة",
"upload.modal.completed.notified-reverse-share-creator": "We have notified the creator of the reverse share. You can also manually share this link with them through other means.",
// END /upload
// /share/[id]
"share.title": "المشاركة {shareId}",
@@ -263,17 +272,19 @@ export default {
"share.error.removed.title": "تمت إزالة المشاركة",
"share.error.not-found.title": "المشاركة غير موجودة",
"share.error.not-found.description": "المشاركة التي تبحث عنها غير موجودة.",
"share.error.access-denied.title": "Private share",
"share.error.access-denied.description": "The current account does not have permission to access this share",
"share.modal.password.title": "كلمة السر مطلوبة",
"share.modal.password.description": "للوصول إلى هذه المشاركة الرجاء إدخال كلمة سر المشاركة.",
"share.modal.password.description": "Please enter the password to acces this share.",
"share.modal.password": "كلمة السر",
"share.modal.error.invalid-password": "كلمة السر غير صحيحة",
"share.button.download-all": "تنزيل الكل",
"share.notify.download-all-preparing": "يتم تحضير المشاركة. حاول مرة أخرى في بضع دقائق.",
"share.notify.download-all-preparing": "The share is being prepared. Please try again in a few minutes.",
"share.modal.file-link": "رابط الملف",
"share.table.name": "الاسم",
"share.table.size": "الحجم",
"share.modal.file-preview.error.not-supported.title": "المعاينة غير مدعومة",
"share.modal.file-preview.error.not-supported.description": "معاينة هذا النوع من الملفات غير مدعومة. الرجاء تنزيل الملف لعرضه.",
"share.modal.file-preview.error.not-supported.description": "Previews are not supported for this type of files. Please download the file to view it.",
// END /share/[id]
// /share/[id]/edit
"share.edit.title": "تحرير {shareId}",
@@ -292,62 +303,70 @@ export default {
"admin.config.general.app-name.description": "اسم التطبيق",
"admin.config.general.app-url": "رابط التطبيق",
"admin.config.general.app-url.description": "الرابط الذي تكون مشاركة Pingvin صالحة عليه",
"admin.config.general.secure-cookies": "Secure cookies",
"admin.config.general.secure-cookies.description": "Whether to set the secure flag on cookies. If enabled, the site will not function when accessed over HTTP.",
"admin.config.general.show-home-page": "إظهار الصفحة الرئيسية",
"admin.config.general.show-home-page.description": "تحديد ما إذا كان سيتم عرض الصفحة الرئيسية",
"admin.config.general.session-duration": "Session Duration",
"admin.config.general.session-duration.description": "Time in hours after which a user must log in again (default: 3 months).",
"admin.config.general.session-duration": "مدة الجلسة",
"admin.config.general.session-duration.description": "الوقت بالساعات الذي يجب على المستخدم بعده إعادة تسجيل الدخول (الافتراضي: 3 أشهر).",
"admin.config.general.logo": "الشعار",
"admin.config.general.logo.description": "يمكنك تغيير شعارك عن طريق تحميل صورة جديدة. يجب أن تكون الصورة PNG ويجب أن يكون تنسيقها 1:1.",
"admin.config.general.logo.placeholder": "اختر صورة",
"admin.config.email.enable-share-email-recipients": "تفعيل مستلمي البريد الإلكتروني لهذه المشاركة",
"admin.config.email.enable-share-email-recipients.description": "السماح لرسائل البريد بأن تُشارك المستلمين. لا تفعّل هذا الخيار ما لم تفعّل SMTP مسبقًا.",
"admin.config.email.enable-share-email-recipients": "Enable email recipient sharing",
"admin.config.email.enable-share-email-recipients.description": "Whether to allow email sharing with recipients. Only enable this if SMTP is activated.",
"admin.config.email.share-recipients-subject": "عنوان الرسالة لمستلمي المشاركة",
"admin.config.email.share-recipients-subject.description": "عنوان البريد الذي سيُرسَل لمستقبِلي المشاركة.",
"admin.config.email.share-recipients-message": "رسالتك لمستقبِلي المشاركة",
"admin.config.email.share-recipients-message.description": "الرسالة التي ستُرسل لمستقبِلي المشاركة. يمكنك استخدام هذه المتغيرات:\n{creator} - اسم المستخدم الذي أنشأ المشاركة\n{shareUrl} - رابط المشاركة\n{desc} - وصف المشاركة\n{expires} - تاريخ انتهاء صلاحية المشاركة\nستتم كتابة قيم هذه المتغيرات تلقائيًا.",
"admin.config.email.share-recipients-message.description": "Message which gets sent to the share recipients. Available variables:\n {creator} - The username of the creator of the share\n {creatorEmail} - The email of the creator of the share\n {shareUrl} - The URL of the share\n {desc} - The description of the share\n {expires} - The expiration date of the share\n These variables will be replaced with the actual value.",
"admin.config.email.reverse-share-subject": "عنوان المشاركة العكسية",
"admin.config.email.reverse-share-subject.description": "عنوان البريد الذي سيُرسل عندما يُنشئ شخص ما مشاركةً باستخدام رابط المشاركة العكسية الخاص بك.",
"admin.config.email.reverse-share-subject.description": "Subject of the sent email when someone created a share with your reverse share link.",
"admin.config.email.reverse-share-message": "رسالة المشاركة العكسية",
"admin.config.email.reverse-share-message.description": "الرسالة التي ستُرسل عندما يُنشئ شخص ما مشاركة باستخدام رابط المشاركة الخاص بك. سيُوضع اسم المُنشِئ ورابط المشاركة مكان {shareUrl}.",
"admin.config.email.reset-password-subject": "رسالة إعادة تعيين كلمة السر",
"admin.config.email.reset-password-subject.description": "عنوان البريد الذي سيُرسل حين يطلب مستخدم ما إعادة تعيين كلمة سرّه.",
"admin.config.email.reset-password-subject.description": "Subject of the sent email when a user requests a password reset.",
"admin.config.email.reset-password-message": "رسالة إعادة تعيين كلمة السر",
"admin.config.email.reset-password-message.description": "الرسالة التي ستُرسل عندما يطلب المستخدم إعادة تعيين كلمة سرّه. سيُوضع رابط إعادة تعيين كلمة السر مكان {url}.",
"admin.config.email.invite-subject": "عنوان الدعوة",
"admin.config.email.invite-subject.description": "عنوان البريد الذي سيُرسل عندما يقوم المشرف بدعوة مستخدم ما.",
"admin.config.email.invite-subject.description": "Subject of the sent email when an admin invites a user.",
"admin.config.email.invite-message": "رسالة الدعوة",
"admin.config.email.invite-message.description": "الرسالة التي ستُرسل عندما يدعو مشرفٌ مستخدمًا. سيُوضع رابط الدعوة مكان {url} وكلمة السر مكان {password}.",
"admin.config.email.invite-message.description": "Message which gets sent when an admin invites a user. {url} will be replaced with the invite URL, {email} with the email and {password} with the users password.",
"admin.config.share.allow-registration": "السماح بالتسجيل",
"admin.config.share.allow-registration.description": "إتاحة تسجيل حساب جديد",
"admin.config.share.allow-unauthenticated-shares": "السماح بالمشاركات غير المصادق عليها",
"admin.config.share.allow-unauthenticated-shares.description": "إتاحة إنشاء المشاركات للمستخدمين غير الموثقين",
"admin.config.share.max-expiration": "أبعد زمن لانتهاء الصلاحية",
"admin.config.share.max-expiration.description": "أطول زمن لانتهاء صلاحية المشاركات بالساعات. الصفر يعني أن المشاركة لن تنتهي صلاحيتها.",
"admin.config.share.share-id-length": "Default share ID length",
"admin.config.share.share-id-length.description": "Default length for the generated ID of a share. This value is also used to generate links for reverse shares. A value below 8 is not considered secure.",
"admin.config.share.max-size": "أكبر حجم",
"admin.config.share.max-size.description": "أكبر حجم للمشاركة مقيسًا بالبايت",
"admin.config.share.zip-compression-level": "مستوى ضغط الZip",
"admin.config.share.zip-compression-level.description": "ضبط الميزان بين حجم الملف وسرعة الضغط. يمكنك إدخال قيم بين 0 إلى 9، حيث 0 تعني بدون ضغط و9 تعني أقصى ضغط. ",
"admin.config.share.chunk-size": "حجم القطعة",
"admin.config.share.chunk-size.description": "ضبط حجم القطعة (بالبايت) لملفاتك المرفوعة للموازنة بين الكفاءة والفعالية حسب قوة اتصالك بالإنترنت. القطع الأصغر يمكن أن ترفع معدل النجاح في حال كان اتصالك بالإنترنت غير مستقر، بينما القطع الأكبر يمكنها أن تُسرّع رفع الملفات في حال كان الاتصال بالإنترنت مستقرًا.",
"admin.config.share.chunk-size.description": "Adjust the chunk size (in bytes) for your uploads to balance efficiency and reliability according to your internet connection. Smaller chunks can enhance success rates for unstable connections, while larger chunks make uploads faster for stable connections.",
"admin.config.share.auto-open-share-modal": "Auto open create share modal",
"admin.config.share.auto-open-share-modal.description": "The share creation modal automatically appears when a user selects files, eliminating the need to manually click the button.",
"admin.config.smtp.enabled": "مفعل",
"admin.config.smtp.enabled": "Enable",
"admin.config.smtp.enabled.description": "تفعيل الـSMTP. لا تفعّله إلا إذا قمت بإدخال المضيف، والمنفذ، والبريد الإلكتروني، واسم المستخدم، وكلمة السر لخادم الـSMTP.",
"admin.config.smtp.host": "المُضيف",
"admin.config.smtp.host.description": "مضيف خادم الـSMTP",
"admin.config.smtp.port": "المنفذ",
"admin.config.smtp.port.description": "منفذ خادم الـSMTP",
"admin.config.smtp.email": "البريد الإلكتروني",
"admin.config.smtp.email.description": "عنوان البريد الذي ستُرسَل الرسائل منه",
"admin.config.smtp.email.description": "Email address from wich the emails get sent",
"admin.config.smtp.username": "اسم المستخدم",
"admin.config.smtp.username.description": "اسم المستخدم لخادم الـSMTP",
"admin.config.smtp.password": "كلمة السر",
"admin.config.smtp.password.description": "كلمة السر لخادم الـSMTP",
"admin.config.smtp.button.test": "إرسال رسالة بريد تجريبية",
"admin.config.smtp.allow-unauthorized-certificates": "Trust unauthorized SMTP server certificates",
"admin.config.smtp.allow-unauthorized-certificates.description": "Only set this to true if you need to trust self signed certificates.",
"admin.config.oauth.allow-registration": "السماح بتسجيل الحسابات الجديدة",
"admin.config.oauth.allow-registration.description": "السماح للمستخدمين بالدخول بواسطة حساباتهم الاجتماعية",
"admin.config.oauth.ignore-totp": "تجاهل TOTP",
"admin.config.oauth.ignore-totp.description": "تجاهل TOTP إذا دخل المستخدم بحسابه الاجتماعي",
"admin.config.oauth.disable-password": "تعطيل تسجيل الدخول باستخدام كلمة السر",
"admin.config.oauth.disable-password.description": "Whether to disable password login\nMake sure that an OAuth provider is properly configured before activating this configuration to avoid being locked out.",
"admin.config.oauth.github-enabled": "GitHub",
"admin.config.oauth.github-enabled.description": "تفعيل خيار الدخول بحساب GitHub",
"admin.config.oauth.github-client-id": "GitHub Client ID",
@@ -370,6 +389,8 @@ export default {
"admin.config.oauth.microsoft-client-secret.description": "الرّمز السرّي للعميل لتطبيق Microsoft OAuth",
"admin.config.oauth.discord-enabled": "Discord",
"admin.config.oauth.discord-enabled.description": "تفعيل خيار الدخول بحساب Discord",
"admin.config.oauth.discord-limited-users": "Discord limited users",
"admin.config.oauth.discord-limited-users.description": "Limit signing in to specific users by their Discord ID. Leave it blank to disable.",
"admin.config.oauth.discord-limited-guild": "مُعرِّف خادم Discord المحدود",
"admin.config.oauth.discord-limited-guild.description": "حصر تسجيل الدخول على المستخدمين الموجودين في خادم محدّد. اترك هذا الخيار فارغًا لتعطيله.",
"admin.config.oauth.discord-client-id": "Discord Client ID",
@@ -380,12 +401,41 @@ export default {
"admin.config.oauth.oidc-enabled.description": "تفعيل الدخول باستخدام OpenID Connect",
"admin.config.oauth.oidc-discovery-uri": "OpenID Connect Discovery URI",
"admin.config.oauth.oidc-discovery-uri.description": "رابط الاستكشاف لتطبيق OpenID Connect OAuth",
"admin.config.oauth.oidc-sign-out": "Sign out from OpenID Connect",
"admin.config.oauth.oidc-sign-out.description": "Whether the “Sign out” button will sign out from the OpenID Connect provider",
"admin.config.oauth.oidc-scope": "OpenID Connect scope",
"admin.config.oauth.oidc-scope.description": "Scopes which should be requested from the OpenID Connect provider.",
"admin.config.oauth.oidc-username-claim": "OpenID Connect username claim",
"admin.config.oauth.oidc-username-claim.description": "طلب اسم المستخدم في رمز معرف OpenID Connect. إذا كنت لا تعرف معنى هذا الإعداد، اتركه فارغًا.",
"admin.config.oauth.oidc-role-path": "Path to roles in OpenID Connect token",
"admin.config.oauth.oidc-role-path.description": "Must be a valid JMES path referencing an array of roles. " + "Managing access rights using OpenID Connect roles is only recommended if no other identity provider is configured and password login is disabled. " + "Leave it blank if you don't know what this config is.",
"admin.config.oauth.oidc-role-general-access": "OpenID Connect role for general access",
"admin.config.oauth.oidc-role-general-access.description": "Role required for general access. Must be present in a users roles for them to log in. " + "Leave it blank if you don't know what this config is.",
"admin.config.oauth.oidc-role-admin-access": "OpenID Connect role for admin access",
"admin.config.oauth.oidc-role-admin-access.description": "Role required for administrative access. Must be present in a users roles for them to access the admin panel. " + "Leave it blank if you don't know what this config is.",
"admin.config.oauth.oidc-client-id": "OpenID Connect Client ID",
"admin.config.oauth.oidc-client-id.description": "معرف العميل لتطبيق OpenID Connect OAuth",
"admin.config.oauth.oidc-client-secret": "OpenID Connect Client secret",
"admin.config.oauth.oidc-client-secret.description": "الرّمز السرّي للعميل لتطبيق OpenID Connect OAuth",
"admin.config.category.ldap": "LDAP",
"admin.config.ldap.enabled": "Enable LDAP",
"admin.config.ldap.enabled.description": "Use LDAP authentication for user login",
"admin.config.ldap.url": "Server URL",
"admin.config.ldap.url.description": "URL of the LDAP server",
"admin.config.ldap.bind-dn": "Bind DN",
"admin.config.ldap.bind-dn.description": "Default user used to perform the user search",
"admin.config.ldap.bind-password": "Bind password",
"admin.config.ldap.bind-password.description": "Password used to perform the user search",
"admin.config.ldap.search-base": "User base",
"admin.config.ldap.search-base.description": "Base location, where the user search will be performed",
"admin.config.ldap.search-query": "User query",
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
"admin.config.ldap.admin-groups": "Admin group",
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
"admin.config.ldap.field-name-member-of": "User groups attribute name",
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
"admin.config.ldap.field-name-email": "User email attribute name",
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
// 404
"404.description": "هذه الصفحة غير موجودة.",
"404.button.home": "أعدني للصفحة الرئيسية",
@@ -400,10 +450,10 @@ export default {
"error.msg.no_user": "المستخدم المرتبط بهذا الحساب {0} غير موجود.",
"error.msg.no_email": "لا يمكن الحصول على عنوان البريد الإلكتروني من هذا الحساب {0}.",
"error.msg.already_linked": "حساب {0} هذا مرتبط بالفعل بحساب آخر.",
"error.msg.not_linked": "لم يتم ربط حساب {0} هذا بأي حساب حتى الآن.",
"error.msg.not_linked": "This {0} account hasn't been linked to any account yet.",
"error.msg.unverified_account": "لم يتم التحقق من حساب {0} هذا، يرجى المحاولة مرة أخرى بعد التحقق.",
"error.msg.discord_guild_permission_denied": "غير مسموح لك بتسجيل الدخول.",
"error.msg.cannot_get_user_info": "فشلت عملية جلب معلومات المستخدم الخاصة بك من حساب {0} هذا.",
"error.msg.user_not_allowed": "غير مسموح لك بتسجيل الدخول.",
"error.msg.cannot_get_user_info": "Cannot get your user info from this {0} account.",
"error.param.provider_github": "GitHub",
"error.param.provider_google": "Google",
"error.param.provider_microsoft": "Microsoft",
@@ -421,8 +471,9 @@ export default {
"common.button.generate": "توليد",
"common.button.done": "تم",
"common.text.link": "الرابط",
"common.text.navigate-to-link": "الذهاب إلى الرابط",
"common.text.navigate-to-link": "Visit link",
"common.text.or": "أو",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "العودة",
"common.button.go-home": "العودة للصفحة الرئيسية",
"common.notify.copied": "تم نسخ الرابط إلى الحافظة",
@@ -432,6 +483,8 @@ export default {
"common.error.invalid-email": "عنوان البريد غير صحيح",
"common.error.too-short": "يجب أن يكون على الأقل {length} حرفًا",
"common.error.too-long": "يجب أن يكون على الأكثر {length} حرفًا",
"common.error.number-too-small": "Must be at least {min}",
"common.error.number-too-large": "Must be at most {max}",
"common.error.exact-length": "يجب أن يكون بالضبط {length} حرفًا",
"common.error.invalid-number": "يجب أن يكون رقماً",
"common.error.field-required": "هذا الحقل مطلوب"

View File

@@ -0,0 +1,491 @@
export default {
// Navbar
"navbar.upload": "Upload",
"navbar.signin": "Sign in",
"navbar.home": "Home",
"navbar.signup": "Sign up",
"navbar.links.shares": "My shares",
"navbar.links.reverse": "Обратни споделяния",
"navbar.avatar.account": "My account",
"navbar.avatar.admin": "Administration",
"navbar.avatar.signout": "Sign out",
// END navbar
// /
"home.title": "A <h>self-hosted</h> file sharing platform.",
"home.description": "Do you really want to give your personal files in the hand of third parties like WeTransfer?",
"home.bullet.a.name": "Self-Hosted",
"home.bullet.a.description": "Host Pingvin Share on your own machine.",
"home.bullet.b.name": "Privacy",
"home.bullet.b.description": "Your files are yours and will never be accessed by third parties.",
"home.bullet.c.name": "No annoying file size limit",
"home.bullet.c.description": "Upload files as big as you want. Only your hard drive will be your limit.",
"home.button.start": "Get started",
"home.button.source": "Source code",
// END /
// /auth/signin
"signin.title": "Welcome back",
"signin.description": "You don't have an account yet?",
"signin.button.signup": "Sign up",
"signin.input.email-or-username": "Email or username",
"signin.input.email-or-username.placeholder": "Your email or username",
"signin.input.password": "Password",
"signin.input.password.placeholder": "Your password",
"signin.button.submit": "Вписване",
"signIn.notify.totp-required.title": "Two-factor authentication required",
"signIn.notify.totp-required.description": "Please enter your two-factor authentication code",
"signIn.oauth.or": "OR",
"signIn.oauth.signInWith": "Sign in with",
"signIn.oauth.github": "GitHub",
"signIn.oauth.google": "Google",
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
// END /auth/signin
// /auth/signup
"signup.title": "Create an account",
"signup.description": "Already have an account?",
"signup.button.signin": "Sign in",
"signup.input.username": "Username",
"signup.input.username.placeholder": "Your username",
"signup.input.email": "Email",
"signup.input.email.placeholder": "Your email",
"signup.button.submit": "Let's get started",
// END /auth/signup
// /auth/totp
"totp.title": "TOTP Authentication",
"totp.button.signIn": "Sign in",
// END /auth/totp
// /auth/reset-password
"resetPassword.title": "Forgot your password?",
"resetPassword.description": "Enter your email to reset your password.",
"resetPassword.notify.success": "A message with a link to reset your password has been sent if the provided email exists.",
"resetPassword.button.back": "Back to sign in page",
"resetPassword.text.resetPassword": "Reset password",
"resetPassword.text.enterNewPassword": "Въведете нова парола",
"resetPassword.input.password": "Нова парола",
"resetPassword.notify.passwordReset": "Your password has been successfully reset.",
// /account
"account.title": "My account",
"account.card.info.title": "Account info",
"account.card.info.username": "Username",
"account.card.info.email": "Email",
"account.notify.info.success": "Account updated successfully",
"account.card.password.title": "Password",
"account.card.password.old": "Old password",
"account.card.password.new": "New password",
"account.card.password.noPasswordSet": "You do not have a password set. To sign in using your email and password, you need to create a password.",
"account.notify.password.success": "Password changed successfully",
"account.card.oauth.title": "Social login",
"account.card.oauth.github": "GitHub",
"account.card.oauth.google": "Google",
"account.card.oauth.microsoft": "Microsoft",
"account.card.oauth.discord": "Discord",
"account.card.oauth.oidc": "OpenID",
"account.card.oauth.link": "Link",
"account.card.oauth.unlink": "Unlink",
"account.card.oauth.unlinked": "Unlinked",
"account.modal.unlink.title": "Unlink account",
"account.modal.unlink.description": "Unlinking your social accounts may cause you to lose your account if you don't remember your login credentials",
"account.notify.oauth.unlinked.success": "Unlinked successfully",
"account.card.security.title": "Security",
"account.card.security.totp.enable.description": "Enter your current password to start enabling TOTP",
"account.card.security.totp.disable.description": "Enter your current password to disable TOTP",
"account.card.security.totp.button.start": "Start",
"account.modal.totp.title": "Enable TOTP",
"account.modal.totp.step1": "Step 1: Add your authenticator",
"account.modal.totp.step2": "Step 2: Validate your code",
"account.modal.totp.enterManually": "Enter manually",
"account.modal.totp.code": "Code",
"common.button.clickToCopy": "Click to copy",
"account.modal.totp.verify": "Verify",
"account.notify.totp.disable": "TOTP disabled successfully",
"account.notify.totp.enable": "TOTP enabled successfully",
"account.card.language.title": "Language",
"account.card.language.description": "The project is translated by the community. Some languages might be incomplete.",
"account.card.color.title": "Color scheme",
// ThemeSwitcher.tsx
"account.theme.dark": "Dark",
"account.theme.light": "Light",
"account.theme.system": "System",
"account.button.delete": "Delete Account",
"account.modal.delete.title": "Delete Account",
"account.modal.delete.description": "Do you really want to delete your account including all your active shares?",
// END /account
// /account/shares
"account.shares.title": "My shares",
"account.shares.title.empty": "It's empty here 👀",
"account.shares.description.empty": "You don't have any shares.",
"account.shares.button.create": "Create one",
"account.shares.info.title": "Share informations",
"account.shares.table.id": "ID",
"account.shares.table.name": "Име",
"account.shares.table.description": "Description",
"account.shares.table.visitors": "Посетители",
"account.shares.table.expiresAt": "Expires on",
"account.shares.table.createdAt": "Created on",
"account.shares.table.size": "Size",
"account.shares.modal.share-informations": "Share informations",
"account.shares.modal.share-link": "Share link",
"account.shares.modal.delete.title": "Delete share: {share}",
"account.shares.modal.delete.description": "Do you really want to delete this share?",
// END /account/shares
// /account/reverseShares
"account.reverseShares.title": "Reverse shares",
"account.reverseShares.description": "A reverse share allows you to generate a unique URL that allows external users to create a share.",
"account.reverseShares.title.empty": "It's empty here 👀",
"account.reverseShares.description.empty": "You don't have any reverse shares.",
// showCreateReverseShareModal.tsx
"account.reverseShares.modal.title": "Create reverse share",
"account.reverseShares.modal.expiration.label": "Expiration",
"account.reverseShares.modal.expiration.minute-singular": "Minute",
"account.reverseShares.modal.expiration.minute-plural": "Minutes",
"account.reverseShares.modal.expiration.hour-singular": "Hour",
"account.reverseShares.modal.expiration.hour-plural": "Hours",
"account.reverseShares.modal.expiration.day-singular": "Day",
"account.reverseShares.modal.expiration.day-plural": "Days",
"account.reverseShares.modal.expiration.week-singular": "Week",
"account.reverseShares.modal.expiration.week-plural": "Weeks",
"account.reverseShares.modal.expiration.month-singular": "Month",
"account.reverseShares.modal.expiration.month-plural": "Months",
"account.reverseShares.modal.expiration.year-singular": "Year",
"account.reverseShares.modal.expiration.year-plural": "Years",
"account.reverseShares.modal.max-size.label": "Max share size",
"account.reverseShares.modal.send-email": "Send email notifications",
"account.reverseShares.modal.send-email.description": "Sends you an email notification when a share is created with this reverse share link.",
"account.reverseShares.modal.simplified": "Simple mode",
"account.reverseShares.modal.simplified.description": "Make it easy for the person uploading the file to share it with you. They will only be able to customize the name and description of the share.",
"account.reverseShares.modal.public-access": "Public access",
"account.reverseShares.modal.public-access.description": "Make the shares created with this reverse share public. If disabled, only you and the share creator will have access to view it.",
"account.reverseShares.modal.max-use.label": "Max uses",
"account.reverseShares.modal.max-use.description": "The maximum amount of times this URL can be used to create a share.",
"account.reverseShare.never-expires": "This reverse share will never expire.",
"account.reverseShare.expires-on": "This reverse share will expire on {expiration}.",
"account.reverseShares.table.no-shares": "No shares created yet",
"account.reverseShares.table.count.singular": "share",
"account.reverseShares.table.count.plural": "shares",
"account.reverseShares.table.shares": "Shares",
"account.reverseShares.table.remaining": "Remaining uses",
"account.reverseShares.table.max-size": "Max share size",
"account.reverseShares.table.expires": "Expires at",
"account.reverseShares.modal.reverse-share-link": "Reverse share link",
"account.reverseShares.modal.delete.title": "Delete reverse share",
"account.reverseShares.modal.delete.description": "Do you really want to delete this reverse share? If you do, the associated shares will be deleted as well.",
// END /account/reverseShares
// /admin
"admin.title": "Administration",
"admin.button.users": "User management",
"admin.button.shares": "Share management",
"admin.button.config": "Configuration",
"admin.version": "Version",
// END /admin
// /admin/users
"admin.users.title": "User management",
"admin.users.table.username": "Username",
"admin.users.table.email": "Email",
"admin.users.table.admin": "Admin",
"admin.users.edit.update.title": "Edit user: {username}",
"admin.users.edit.update.admin-privileges": "Admin privileges",
"admin.users.edit.update.change-password.title": "Change password",
"admin.users.edit.update.change-password.field": "New password",
"admin.users.edit.update.change-password.button": "Save new password",
"admin.users.edit.update.notify.password.success": "Password changed successfully",
"admin.users.edit.delete.title": "Delete user: {username} ?",
"admin.users.edit.delete.description": "Do you really want to delete this user and all their shares?",
// showCreateUserModal.tsx
"admin.users.modal.create.title": "Create user",
"admin.users.modal.create.username": "Username",
"admin.users.modal.create.email": "Email",
"admin.users.modal.create.password": "Password",
"admin.users.modal.create.manual-password": "Set password manually",
"admin.users.modal.create.manual-password.description": "If not checked, the user will receive an email with a link to set their password.",
"admin.users.modal.create.admin": "Admin privileges",
"admin.users.modal.create.admin.description": "If checked, the user will be able to access the admin panel.",
// END /admin/users
// /admin/shares
"admin.shares.title": "Share management",
"admin.shares.table.id": "Share ID",
"admin.shares.table.username": "Creator",
"admin.shares.table.visitors": "Visitors",
"admin.shares.table.expires": "Expires on",
"admin.shares.edit.delete.title": "Delete share: {id}",
"admin.shares.edit.delete.description": "Do you really want to delete this share?",
// END /admin/shares
// /upload
"upload.title": "Upload",
"upload.notify.confirm-leave": "Are you sure you want to leave this page? Your upload will be canceled.",
"upload.notify.generic-error": "An error occurred while finishing your share.",
"upload.notify.count-failed": "{count} files failed to upload. Trying again.",
"upload.reverse-share.error.invalid.title": "Invalid reverse share link",
"upload.reverse-share.error.invalid.description": "This reverse share has expired or is invalid.",
// Dropzone.tsx
"upload.dropzone.title": "Upload files",
"upload.dropzone.description": "Drag'n'drop files here to start your share. We only accept files up to {maxSize} in total.",
"upload.dropzone.notify.file-too-big": "Your files exceed the maximum share size of {maxSize}.",
// FileList.tsx
"upload.filelist.name": "Name",
"upload.filelist.size": "Size",
// showCreateUploadModal.tsx
"upload.modal.title": "Create Share",
"upload.modal.link.error.invalid": "Can only contain letters, numbers, underscores, and hyphens",
"upload.modal.link.error.taken": "This link is already in use",
"upload.modal.not-signed-in": "You're not signed in",
"upload.modal.not-signed-in-description": "You will be unable to delete your share manually and view the visitor count.",
"upload.modal.expires.never": "never",
"upload.modal.expires.never-long": "Permanent share",
"upload.modal.expires.error.too-long": "Expiration date exceeds the maximum of {max}.",
"upload.modal.link.label": "Link",
"upload.modal.expires.label": "Expiration",
"upload.modal.expires.minute-singular": "Minute",
"upload.modal.expires.minute-plural": "Minutes",
"upload.modal.expires.hour-singular": "Hour",
"upload.modal.expires.hour-plural": "Hours",
"upload.modal.expires.day-singular": "Day",
"upload.modal.expires.day-plural": "Days",
"upload.modal.expires.week-singular": "Week",
"upload.modal.expires.week-plural": "Weeks",
"upload.modal.expires.month-singular": "Month",
"upload.modal.expires.month-plural": "Months",
"upload.modal.expires.year-singular": "Year",
"upload.modal.expires.year-plural": "Years",
"upload.modal.accordion.name-and-description.title": "Name and description",
"upload.modal.accordion.name-and-description.name.placeholder": "Name",
"upload.modal.accordion.name-and-description.description.placeholder": "Note for the recipients of this share",
"upload.modal.accordion.email.title": "Email recipients",
"upload.modal.accordion.email.placeholder": "Enter email recipients",
"upload.modal.accordion.email.invalid-email": "Invalid email address",
"upload.modal.accordion.security.title": "Security options",
"upload.modal.accordion.security.password.label": "Password protection",
"upload.modal.accordion.security.password.placeholder": "No password",
"upload.modal.accordion.security.max-views.label": "Maximum views",
"upload.modal.accordion.security.max-views.placeholder": "No limit",
// showCompletedUploadModal.tsx
"upload.modal.completed.never-expires": "This share will never expire.",
"upload.modal.completed.expires-on": "This share will expire on {expiration}.",
"upload.modal.completed.share-ready": "Share ready",
"upload.modal.completed.notified-reverse-share-creator": "We have notified the creator of the reverse share. You can also manually share this link with them through other means.",
// END /upload
// /share/[id]
"share.title": "Share {shareId}",
"share.description": "Look what I've shared with you!",
"share.error.visitor-limit-exceeded.title": "Visitor limit exceeded",
"share.error.visitor-limit-exceeded.description": "The visitor limit from this share has been exceeded.",
"share.error.removed.title": "Share removed",
"share.error.not-found.title": "Share not found",
"share.error.not-found.description": "The share you're looking for doesn't exist.",
"share.error.access-denied.title": "Private share",
"share.error.access-denied.description": "The current account does not have permission to access this share",
"share.modal.password.title": "Password required",
"share.modal.password.description": "Please enter the password to acces this share.",
"share.modal.password": "Password",
"share.modal.error.invalid-password": "Invalid password",
"share.button.download-all": "Download all",
"share.notify.download-all-preparing": "The share is being prepared. Please try again in a few minutes.",
"share.modal.file-link": "File link",
"share.table.name": "Name",
"share.table.size": "Size",
"share.modal.file-preview.error.not-supported.title": "Preview not supported",
"share.modal.file-preview.error.not-supported.description": "Previews are not supported for this type of files. Please download the file to view it.",
// END /share/[id]
// /share/[id]/edit
"share.edit.title": "Edit {shareId}",
"share.edit.append-upload": "Append file",
"share.edit.notify.generic-error": "An error occurred while finishing your share.",
"share.edit.notify.save-success": "Share updated successfully",
// END /share/[id]/edit
// /admin/config
"admin.config.title": "Configuration",
"admin.config.category.general": "General",
"admin.config.category.share": "Share",
"admin.config.category.email": "Email",
"admin.config.category.smtp": "SMTP",
"admin.config.category.oauth": "Social Login",
"admin.config.general.app-name": "App name",
"admin.config.general.app-name.description": "Name of the application",
"admin.config.general.app-url": "App URL",
"admin.config.general.app-url.description": "On which URL Pingvin Share is available",
"admin.config.general.secure-cookies": "Secure cookies",
"admin.config.general.secure-cookies.description": "Whether to set the secure flag on cookies. If enabled, the site will not function when accessed over HTTP.",
"admin.config.general.show-home-page": "Show home page",
"admin.config.general.show-home-page.description": "Whether to show the home page",
"admin.config.general.session-duration": "Session Duration",
"admin.config.general.session-duration.description": "Time in hours after which a user must log in again (default: 3 months).",
"admin.config.general.logo": "Logo",
"admin.config.general.logo.description": "Change your logo by uploading a new image. The image must be a PNG and should have the format 1:1.",
"admin.config.general.logo.placeholder": "Pick image",
"admin.config.email.enable-share-email-recipients": "Enable email recipient sharing",
"admin.config.email.enable-share-email-recipients.description": "Whether to allow email sharing with recipients. Only enable this if SMTP is activated.",
"admin.config.email.share-recipients-subject": "Share recipients subject",
"admin.config.email.share-recipients-subject.description": "Subject of the email which gets sent to the share recipients.",
"admin.config.email.share-recipients-message": "Share recipients message",
"admin.config.email.share-recipients-message.description": "Message which gets sent to the share recipients. Available variables:\n {creator} - The username of the creator of the share\n {creatorEmail} - The email of the creator of the share\n {shareUrl} - The URL of the share\n {desc} - The description of the share\n {expires} - The expiration date of the share\n These variables will be replaced with the actual value.",
"admin.config.email.reverse-share-subject": "Reverse share subject",
"admin.config.email.reverse-share-subject.description": "Subject of the sent email when someone created a share with your reverse share link.",
"admin.config.email.reverse-share-message": "Reverse share message",
"admin.config.email.reverse-share-message.description": "Message which gets sent when someone created a share with your reverse share link. {shareUrl} will be replaced with the creator's name and the share URL.",
"admin.config.email.reset-password-subject": "Reset password subject",
"admin.config.email.reset-password-subject.description": "Subject of the sent email when a user requests a password reset.",
"admin.config.email.reset-password-message": "Reset password message",
"admin.config.email.reset-password-message.description": "Message which gets sent when a user requests a password reset. {url} will be replaced with the reset password URL.",
"admin.config.email.invite-subject": "Invite subject",
"admin.config.email.invite-subject.description": "Subject of the sent email when an admin invites a user.",
"admin.config.email.invite-message": "Invite message",
"admin.config.email.invite-message.description": "Message which gets sent when an admin invites a user. {url} will be replaced with the invite URL, {email} with the email and {password} with the users password.",
"admin.config.share.allow-registration": "Allow registration",
"admin.config.share.allow-registration.description": "Whether registration is allowed",
"admin.config.share.allow-unauthenticated-shares": "Allow unauthenticated shares",
"admin.config.share.allow-unauthenticated-shares.description": "Whether unauthenticated users can create shares",
"admin.config.share.max-expiration": "Max expiration",
"admin.config.share.max-expiration.description": "Maximum share expiration in hours. Set to 0 to allow unlimited expiration.",
"admin.config.share.share-id-length": "Default share ID length",
"admin.config.share.share-id-length.description": "Default length for the generated ID of a share. This value is also used to generate links for reverse shares. A value below 8 is not considered secure.",
"admin.config.share.max-size": "Max size",
"admin.config.share.max-size.description": "Maximum share size in bytes",
"admin.config.share.zip-compression-level": "Zip compression level",
"admin.config.share.zip-compression-level.description": "Adjust the level to balance between file size and compression speed. Valid values range from 0 to 9, with 0 being no compression and 9 being maximum compression. ",
"admin.config.share.chunk-size": "Chunk size",
"admin.config.share.chunk-size.description": "Adjust the chunk size (in bytes) for your uploads to balance efficiency and reliability according to your internet connection. Smaller chunks can enhance success rates for unstable connections, while larger chunks make uploads faster for stable connections.",
"admin.config.share.auto-open-share-modal": "Auto open create share modal",
"admin.config.share.auto-open-share-modal.description": "The share creation modal automatically appears when a user selects files, eliminating the need to manually click the button.",
"admin.config.smtp.enabled": "Enable",
"admin.config.smtp.enabled.description": "Whether SMTP is enabled. Only set this to true if you entered the host, port, email, user and password of your SMTP server.",
"admin.config.smtp.host": "Host",
"admin.config.smtp.host.description": "Host of the SMTP server",
"admin.config.smtp.port": "Port",
"admin.config.smtp.port.description": "Port of the SMTP server",
"admin.config.smtp.email": "Email",
"admin.config.smtp.email.description": "Email address from wich the emails get sent",
"admin.config.smtp.username": "Username",
"admin.config.smtp.username.description": "Username of the SMTP server",
"admin.config.smtp.password": "Password",
"admin.config.smtp.password.description": "Password of the SMTP server",
"admin.config.smtp.button.test": "Send test email",
"admin.config.smtp.allow-unauthorized-certificates": "Trust unauthorized SMTP server certificates",
"admin.config.smtp.allow-unauthorized-certificates.description": "Only set this to true if you need to trust self signed certificates.",
"admin.config.oauth.allow-registration": "Allow registration",
"admin.config.oauth.allow-registration.description": "Allow users to register via social login",
"admin.config.oauth.ignore-totp": "Ignore TOTP",
"admin.config.oauth.ignore-totp.description": "Whether to ignore TOTP when user using social login",
"admin.config.oauth.disable-password": "Disable password login",
"admin.config.oauth.disable-password.description": "Whether to disable password login\nMake sure that an OAuth provider is properly configured before activating this configuration to avoid being locked out.",
"admin.config.oauth.github-enabled": "GitHub",
"admin.config.oauth.github-enabled.description": "Whether GitHub login is enabled",
"admin.config.oauth.github-client-id": "GitHub Client ID",
"admin.config.oauth.github-client-id.description": "Client ID of the GitHub OAuth app",
"admin.config.oauth.github-client-secret": "GitHub Client secret",
"admin.config.oauth.github-client-secret.description": "Client secret of the GitHub OAuth app",
"admin.config.oauth.google-enabled": "Google",
"admin.config.oauth.google-enabled.description": "Whether Google login is enabled",
"admin.config.oauth.google-client-id": "Google Client ID",
"admin.config.oauth.google-client-id.description": "Client ID of the Google OAuth app",
"admin.config.oauth.google-client-secret": "Google Client secret",
"admin.config.oauth.google-client-secret.description": "Client secret of the Google OAuth app",
"admin.config.oauth.microsoft-enabled": "Microsoft",
"admin.config.oauth.microsoft-enabled.description": "Whether Microsoft login is enabled",
"admin.config.oauth.microsoft-tenant": "Microsoft Tenant",
"admin.config.oauth.microsoft-tenant.description": "Tenant ID of the Microsoft OAuth app\ncommon: Users with both a personal Microsoft account and a work or school account from Microsoft Entra ID can sign in to the application. organizations: Only users with work or school accounts from Microsoft Entra ID can sign in to the application.\nconsumers: Only users with a personal Microsoft account can sign in to the application.\ndomain name of the Microsoft Entra tenant or the tenant ID in GUID format: Only users from a specific Microsoft Entra tenant (directory members with a work or school account or directory guests with a personal Microsoft account) can sign in to the application.",
"admin.config.oauth.microsoft-client-id": "Microsoft Client ID",
"admin.config.oauth.microsoft-client-id.description": "Client ID of the Microsoft OAuth app",
"admin.config.oauth.microsoft-client-secret": "Microsoft Client secret",
"admin.config.oauth.microsoft-client-secret.description": "Client secret of the Microsoft OAuth app",
"admin.config.oauth.discord-enabled": "Discord",
"admin.config.oauth.discord-enabled.description": "Whether Discord login is enabled",
"admin.config.oauth.discord-limited-users": "Discord limited users",
"admin.config.oauth.discord-limited-users.description": "Limit signing in to specific users by their Discord ID. Leave it blank to disable.",
"admin.config.oauth.discord-limited-guild": "Discord limited server ID",
"admin.config.oauth.discord-limited-guild.description": "Limit signing in to users in a specific server. Leave it blank to disable.",
"admin.config.oauth.discord-client-id": "Discord Client ID",
"admin.config.oauth.discord-client-id.description": "Client ID of the Discord OAuth app",
"admin.config.oauth.discord-client-secret": "Discord Client secret",
"admin.config.oauth.discord-client-secret.description": "Client secret of the Discord OAuth app",
"admin.config.oauth.oidc-enabled": "OpenID Connect",
"admin.config.oauth.oidc-enabled.description": "Whether OpenID Connect login is enabled",
"admin.config.oauth.oidc-discovery-uri": "OpenID Connect Discovery URI",
"admin.config.oauth.oidc-discovery-uri.description": "Discovery URI of the OpenID Connect OAuth app",
"admin.config.oauth.oidc-sign-out": "Sign out from OpenID Connect",
"admin.config.oauth.oidc-sign-out.description": "Whether the “Sign out” button will sign out from the OpenID Connect provider",
"admin.config.oauth.oidc-scope": "OpenID Connect scope",
"admin.config.oauth.oidc-scope.description": "Scopes which should be requested from the OpenID Connect provider.",
"admin.config.oauth.oidc-username-claim": "OpenID Connect username claim",
"admin.config.oauth.oidc-username-claim.description": "Username claim in OpenID Connect ID token. Leave it blank if you don't know what this config is.",
"admin.config.oauth.oidc-role-path": "Path to roles in OpenID Connect token",
"admin.config.oauth.oidc-role-path.description": "Must be a valid JMES path referencing an array of roles. " + "Managing access rights using OpenID Connect roles is only recommended if no other identity provider is configured and password login is disabled. " + "Leave it blank if you don't know what this config is.",
"admin.config.oauth.oidc-role-general-access": "OpenID Connect role for general access",
"admin.config.oauth.oidc-role-general-access.description": "Role required for general access. Must be present in a users roles for them to log in. " + "Leave it blank if you don't know what this config is.",
"admin.config.oauth.oidc-role-admin-access": "OpenID Connect role for admin access",
"admin.config.oauth.oidc-role-admin-access.description": "Role required for administrative access. Must be present in a users roles for them to access the admin panel. " + "Leave it blank if you don't know what this config is.",
"admin.config.oauth.oidc-client-id": "OpenID Connect Client ID",
"admin.config.oauth.oidc-client-id.description": "Client ID of the OpenID Connect OAuth app",
"admin.config.oauth.oidc-client-secret": "OpenID Connect Client secret",
"admin.config.oauth.oidc-client-secret.description": "Client secret of the OpenID Connect OAuth app",
"admin.config.category.ldap": "LDAP",
"admin.config.ldap.enabled": "Enable LDAP",
"admin.config.ldap.enabled.description": "Use LDAP authentication for user login",
"admin.config.ldap.url": "Server URL",
"admin.config.ldap.url.description": "URL of the LDAP server",
"admin.config.ldap.bind-dn": "Bind DN",
"admin.config.ldap.bind-dn.description": "Default user used to perform the user search",
"admin.config.ldap.bind-password": "Bind password",
"admin.config.ldap.bind-password.description": "Password used to perform the user search",
"admin.config.ldap.search-base": "User base",
"admin.config.ldap.search-base.description": "Base location, where the user search will be performed",
"admin.config.ldap.search-query": "User query",
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
"admin.config.ldap.admin-groups": "Admin group",
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
"admin.config.ldap.field-name-member-of": "User groups attribute name",
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
"admin.config.ldap.field-name-email": "User email attribute name",
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
// 404
"404.description": "Oops this page doesn't exist.",
"404.button.home": "Bring me back home",
// error
"error.title": "Error",
"error.description": "Oops!",
"error.button.back": "Go back",
"error.msg.default": "Something went wrong.",
"error.msg.access_denied": "You canceled the authentication process, please try again.",
"error.msg.expired_token": "The authentication process took too long, please try again.",
"error.msg.invalid_token": "Internal Error",
"error.msg.no_user": "User linked to this {0} account doesn't exist.",
"error.msg.no_email": "Can't get email address from this {0} account.",
"error.msg.already_linked": "This {0} account is already linked to another account.",
"error.msg.not_linked": "This {0} account hasn't been linked to any account yet.",
"error.msg.unverified_account": "This {0} account is unverified, please try again after verification.",
"error.msg.user_not_allowed": "You are not allowed to sign in.",
"error.msg.cannot_get_user_info": "Cannot get your user info from this {0} account.",
"error.param.provider_github": "GitHub",
"error.param.provider_google": "Google",
"error.param.provider_microsoft": "Microsoft",
"error.param.provider_discord": "Discord",
"error.param.provider_oidc": "OpenID Connect",
// Common translations
"common.button.save": "Save",
"common.button.create": "Create",
"common.button.submit": "Submit",
"common.button.delete": "Delete",
"common.button.cancel": "Cancel",
"common.button.confirm": "Confirm",
"common.button.disable": "Disable",
"common.button.share": "Share",
"common.button.generate": "Generate",
"common.button.done": "Done",
"common.text.link": "Link",
"common.text.navigate-to-link": "Visit link",
"common.text.or": "or",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "Go back",
"common.button.go-home": "Go home",
"common.notify.copied": "Your link was copied to the clipboard",
"common.success": "Success",
"common.error": "Error",
"common.error.unknown": "An unknown error occurred",
"common.error.invalid-email": "Invalid email address",
"common.error.too-short": "Must be at least {length} characters",
"common.error.too-long": "Must be at most {length} characters",
"common.error.number-too-small": "Must be at least {min}",
"common.error.number-too-large": "Must be at most {max}",
"common.error.exact-length": "Must be exactly {length} characters",
"common.error.invalid-number": "Must be a number",
"common.error.field-required": "This field is required"
};

View File

@@ -0,0 +1,491 @@
export default {
// Navbar
"navbar.upload": "Nahrát",
"navbar.signin": "Přihlásit se",
"navbar.home": "Domů",
"navbar.signup": "Sign up",
"navbar.links.shares": "Má sdílení",
"navbar.links.reverse": "Zpětná sdílení",
"navbar.avatar.account": "Můj účet",
"navbar.avatar.admin": "Administrace",
"navbar.avatar.signout": "Odhlásit se",
// END navbar
// /
"home.title": "<h>Samostatně hostovaná</h> platforma pro sdílení souborů.",
"home.description": "Opravdu chcete dát své osobní soubory do rukou třetích stran, jako je WeTransfer?",
"home.bullet.a.name": "Self-Hosted",
"home.bullet.a.description": "Hostujte Pingvin Share na svém vlastním počítači.",
"home.bullet.b.name": "Soukromí",
"home.bullet.b.description": "Your files are yours and will never be accessed by third parties.",
"home.bullet.c.name": "Žádný otravný limit pro velikost souborů",
"home.bullet.c.description": "Upload files as big as you want. Only your hard drive will be your limit.",
"home.button.start": "Začít",
"home.button.source": "Zdrojový kód",
// END /
// /auth/signin
"signin.title": "Vítejte zpět",
"signin.description": "Ještě nemáte účet?",
"signin.button.signup": "Zaregistrovat se",
"signin.input.email-or-username": "E-mail nebo uživatelské jméno",
"signin.input.email-or-username.placeholder": "Váš e-mail nebo uživatelské jméno",
"signin.input.password": "Heslo",
"signin.input.password.placeholder": "Vaše heslo",
"signin.button.submit": "Přihlásit se",
"signIn.notify.totp-required.title": "Vyžadováno dvoufaktorové ověření",
"signIn.notify.totp-required.description": "Zadejte prosím svůj dvoufaktorový ověřovací kód",
"signIn.oauth.or": "NEBO",
"signIn.oauth.signInWith": "Přihlásit se pomocí",
"signIn.oauth.github": "GitHub",
"signIn.oauth.google": "Google",
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
// END /auth/signin
// /auth/signup
"signup.title": "Vytvořit účet",
"signup.description": "Máte již svůj účet?",
"signup.button.signin": "Přihlásit se",
"signup.input.username": "Uživatelské jméno",
"signup.input.username.placeholder": "Vaše uživatelské jméno",
"signup.input.email": "E-mail",
"signup.input.email.placeholder": "Váš e-mail",
"signup.button.submit": "Pojďme na to",
// END /auth/signup
// /auth/totp
"totp.title": "TOTP ověření",
"totp.button.signIn": "Přihlásit se",
// END /auth/totp
// /auth/reset-password
"resetPassword.title": "Zapomněli jste heslo?",
"resetPassword.description": "Zadejte svůj e-mail pro obnovení hesla.",
"resetPassword.notify.success": "A message with a link to reset your password has been sent if the provided email exists.",
"resetPassword.button.back": "Zpět na přihlašovací stránku",
"resetPassword.text.resetPassword": "Obnovit heslo",
"resetPassword.text.enterNewPassword": "Zadejte své nové heslo",
"resetPassword.input.password": "Nové heslo",
"resetPassword.notify.passwordReset": "Your password has been successfully reset.",
// /account
"account.title": "Můj účet",
"account.card.info.title": "Informace o účtu",
"account.card.info.username": "Uživatelské jméno",
"account.card.info.email": "E-mail",
"account.notify.info.success": "Účet byl úspěšně aktualizován",
"account.card.password.title": "Heslo",
"account.card.password.old": "Staré heslo",
"account.card.password.new": "Nové heslo",
"account.card.password.noPasswordSet": "You do not have a password set. To sign in using your email and password, you need to create a password.",
"account.notify.password.success": "Heslo bylo úspěšně změněno",
"account.card.oauth.title": "Přihlášení přes sociální sítě",
"account.card.oauth.github": "GitHub",
"account.card.oauth.google": "Google",
"account.card.oauth.microsoft": "Microsoft",
"account.card.oauth.discord": "Discord",
"account.card.oauth.oidc": "OpenID",
"account.card.oauth.link": "Odkaz",
"account.card.oauth.unlink": "Odpojit",
"account.card.oauth.unlinked": "Odpojeno",
"account.modal.unlink.title": "Odpojit účet",
"account.modal.unlink.description": "Unlinking your social accounts may cause you to lose your account if you don't remember your login credentials",
"account.notify.oauth.unlinked.success": "Úspěšně odpojeno",
"account.card.security.title": "Zabezpečení",
"account.card.security.totp.enable.description": "Zadejte své současné heslo, abyste mohli povolit TOTP",
"account.card.security.totp.disable.description": "Zadejte své současné heslo pro zakázání TOTP",
"account.card.security.totp.button.start": "Začít",
"account.modal.totp.title": "Povolit TOTP",
"account.modal.totp.step1": "Krok 1: Přidejte váš autentifikátor",
"account.modal.totp.step2": "Krok 2: Ověřte váš kód",
"account.modal.totp.enterManually": "Zadat ručně",
"account.modal.totp.code": "Kód",
"common.button.clickToCopy": "Kliknutím zkopírujete",
"account.modal.totp.verify": "Ověřit",
"account.notify.totp.disable": "TOTP úspěšně zakázáno",
"account.notify.totp.enable": "TOTP úspěšně povoleno",
"account.card.language.title": "Jazyk",
"account.card.language.description": "Projekt je přeložen komunitou. Některé jazyky mohou být neúplné.",
"account.card.color.title": "Barevné schéma",
// ThemeSwitcher.tsx
"account.theme.dark": "Tmavé",
"account.theme.light": "Světlé",
"account.theme.system": "Systémové",
"account.button.delete": "Odstranit účet",
"account.modal.delete.title": "Odstranit účet",
"account.modal.delete.description": "Opravdu chcete odstranit svůj účet včetně všech aktivních sdílení?",
// END /account
// /account/shares
"account.shares.title": "Má sdílení",
"account.shares.title.empty": "Je tu prázdno 👀",
"account.shares.description.empty": "Nemáte žádná sdílení.",
"account.shares.button.create": "Create one",
"account.shares.info.title": "Share informations",
"account.shares.table.id": "ID",
"account.shares.table.name": "Název",
"account.shares.table.description": "Popis",
"account.shares.table.visitors": "Návštěvníci",
"account.shares.table.expiresAt": "Expires on",
"account.shares.table.createdAt": "Created on",
"account.shares.table.size": "Velikost",
"account.shares.modal.share-informations": "Share informations",
"account.shares.modal.share-link": "Odkaz na sdílení",
"account.shares.modal.delete.title": "Delete share: {share}",
"account.shares.modal.delete.description": "Opravdu chcete odstranit toto sdílení?",
// END /account/shares
// /account/reverseShares
"account.reverseShares.title": "Zpětná sdílení",
"account.reverseShares.description": "Zpětné sdílení umožňuje vygenerovat jedinečné URL, které umožní externím uživatelům vytvořit sdílet soubory.",
"account.reverseShares.title.empty": "Je tu prázdno 👀",
"account.reverseShares.description.empty": "Nemáte žádná zpětná sdílení.",
// showCreateReverseShareModal.tsx
"account.reverseShares.modal.title": "Vytvořit zpětné sdílení",
"account.reverseShares.modal.expiration.label": "Expirace",
"account.reverseShares.modal.expiration.minute-singular": "Minuta",
"account.reverseShares.modal.expiration.minute-plural": "Minut",
"account.reverseShares.modal.expiration.hour-singular": "Hodina",
"account.reverseShares.modal.expiration.hour-plural": "Hodin",
"account.reverseShares.modal.expiration.day-singular": "Den",
"account.reverseShares.modal.expiration.day-plural": "Dnů",
"account.reverseShares.modal.expiration.week-singular": "Týden",
"account.reverseShares.modal.expiration.week-plural": "Týdnů",
"account.reverseShares.modal.expiration.month-singular": "Měsíc",
"account.reverseShares.modal.expiration.month-plural": "Měsíců",
"account.reverseShares.modal.expiration.year-singular": "Rok",
"account.reverseShares.modal.expiration.year-plural": "Let",
"account.reverseShares.modal.max-size.label": "Max. velikost sdílení",
"account.reverseShares.modal.send-email": "Send email notifications",
"account.reverseShares.modal.send-email.description": "Sends you an email notification when a share is created with this reverse share link.",
"account.reverseShares.modal.simplified": "Zjednodušený režim",
"account.reverseShares.modal.simplified.description": "Make it easy for the person uploading the file to share it with you. They will only be able to customize the name and description of the share.",
"account.reverseShares.modal.public-access": "Veřejný přístup",
"account.reverseShares.modal.public-access.description": "Make the shares created with this reverse share public. If disabled, only you and the share creator will have access to view it.",
"account.reverseShares.modal.max-use.label": "Max. použití",
"account.reverseShares.modal.max-use.description": "Maximální počet sdílení, která mohou být vytvořena za pomoci tohoto URL.",
"account.reverseShare.never-expires": "Toto zpětné sdílení nikdy nevyprší.",
"account.reverseShare.expires-on": "Toto zpětné sdílení vyprší {expiration}.",
"account.reverseShares.table.no-shares": "Zatím nebyla vytvořena žádná sdílení",
"account.reverseShares.table.count.singular": "sdílení",
"account.reverseShares.table.count.plural": "sdílení",
"account.reverseShares.table.shares": "Sdílení",
"account.reverseShares.table.remaining": "Zbývající použití",
"account.reverseShares.table.max-size": "Max. velikost sdílení",
"account.reverseShares.table.expires": "Vyprší",
"account.reverseShares.modal.reverse-share-link": "Odkaz na zpětné sdílení",
"account.reverseShares.modal.delete.title": "Odstranit zpětné sdílení",
"account.reverseShares.modal.delete.description": "Opravdu chcete odstranit toto zpětné sdílení? Pokud tak učiníte, související sdílené budou také odstraněny.",
// END /account/reverseShares
// /admin
"admin.title": "Administrace",
"admin.button.users": "Správa uživatelů",
"admin.button.shares": "Správa sdílení",
"admin.button.config": "Nastavení",
"admin.version": "Verze",
// END /admin
// /admin/users
"admin.users.title": "Správa uživatelů",
"admin.users.table.username": "Uživatelské jméno",
"admin.users.table.email": "E-mail",
"admin.users.table.admin": "Administrátor",
"admin.users.edit.update.title": "Edit user: {username}",
"admin.users.edit.update.admin-privileges": "Administrátorská práva",
"admin.users.edit.update.change-password.title": "Změnit heslo",
"admin.users.edit.update.change-password.field": "Nové heslo",
"admin.users.edit.update.change-password.button": "Uložit nové heslo",
"admin.users.edit.update.notify.password.success": "Heslo bylo úspěšně změněno",
"admin.users.edit.delete.title": "Delete user: {username} ?",
"admin.users.edit.delete.description": "Do you really want to delete this user and all their shares?",
// showCreateUserModal.tsx
"admin.users.modal.create.title": "Vytvořit uživatele",
"admin.users.modal.create.username": "Uživatelské jméno",
"admin.users.modal.create.email": "E-mail",
"admin.users.modal.create.password": "Heslo",
"admin.users.modal.create.manual-password": "Ručně nastavit heslo",
"admin.users.modal.create.manual-password.description": "Pokud není zaškrtnuto, uživatel obdrží e-mail s odkazem pro nastavení svého hesla.",
"admin.users.modal.create.admin": "Administrátorská práva",
"admin.users.modal.create.admin.description": "Je-li zaškrtnuto, uživatel bude mít přístup k administračnímu panelu.",
// END /admin/users
// /admin/shares
"admin.shares.title": "Správa sdílení",
"admin.shares.table.id": "ID sdílení",
"admin.shares.table.username": "Autor",
"admin.shares.table.visitors": "Návštěvníci",
"admin.shares.table.expires": "Expires on",
"admin.shares.edit.delete.title": "Delete share: {id}",
"admin.shares.edit.delete.description": "Opravdu chcete smazat toto sdílení?",
// END /admin/shares
// /upload
"upload.title": "Nahrát",
"upload.notify.confirm-leave": "Are you sure you want to leave this page? Your upload will be canceled.",
"upload.notify.generic-error": "Při dokončování vašeho sdílení došlo k chybě.",
"upload.notify.count-failed": "{count} souborů se nepodařilo nahrát. Zkouším to znovu.",
"upload.reverse-share.error.invalid.title": "Invalid reverse share link",
"upload.reverse-share.error.invalid.description": "This reverse share has expired or is invalid.",
// Dropzone.tsx
"upload.dropzone.title": "Nahrát soubory",
"upload.dropzone.description": "Drag'n'drop files here to start your share. We only accept files up to {maxSize} in total.",
"upload.dropzone.notify.file-too-big": "Vaše soubory přesahují maximální velikost {maxSize}.",
// FileList.tsx
"upload.filelist.name": "Název",
"upload.filelist.size": "Velikost",
// showCreateUploadModal.tsx
"upload.modal.title": "Vytvořit sdílení",
"upload.modal.link.error.invalid": "Může obsahovat pouze písmena, číslice, podtržítka a pomlčky",
"upload.modal.link.error.taken": "Tento odkaz je již používán",
"upload.modal.not-signed-in": "Nejste přihlášeni",
"upload.modal.not-signed-in-description": "Nebudete moci ručně odstranit své sdílení a zobrazit počet návštěvníků.",
"upload.modal.expires.never": "nikdy",
"upload.modal.expires.never-long": "Permanent share",
"upload.modal.expires.error.too-long": "Expiration date exceeds the maximum of {max}.",
"upload.modal.link.label": "Odkaz",
"upload.modal.expires.label": "Expirace",
"upload.modal.expires.minute-singular": "Minuta",
"upload.modal.expires.minute-plural": "Minut",
"upload.modal.expires.hour-singular": "Hodina",
"upload.modal.expires.hour-plural": "Hodin",
"upload.modal.expires.day-singular": "Den",
"upload.modal.expires.day-plural": "Dnů",
"upload.modal.expires.week-singular": "Týden",
"upload.modal.expires.week-plural": "Týdnů",
"upload.modal.expires.month-singular": "Měsíc",
"upload.modal.expires.month-plural": "Měsíců",
"upload.modal.expires.year-singular": "Rok",
"upload.modal.expires.year-plural": "Let",
"upload.modal.accordion.name-and-description.title": "Název a popis",
"upload.modal.accordion.name-and-description.name.placeholder": "Název",
"upload.modal.accordion.name-and-description.description.placeholder": "Poznámka pro příjemce tohoto sdílení",
"upload.modal.accordion.email.title": "Příjemci e-mailu",
"upload.modal.accordion.email.placeholder": "Zadejte příjemce e-mailu",
"upload.modal.accordion.email.invalid-email": "Neplatná e-mailová adresa",
"upload.modal.accordion.security.title": "Možnosti zabezpečení",
"upload.modal.accordion.security.password.label": "Ochrana heslem",
"upload.modal.accordion.security.password.placeholder": "Bez hesla",
"upload.modal.accordion.security.max-views.label": "Maximální počet zobrazení",
"upload.modal.accordion.security.max-views.placeholder": "Bez omezení",
// showCompletedUploadModal.tsx
"upload.modal.completed.never-expires": "Toto sdílení nikdy nevyprší.",
"upload.modal.completed.expires-on": "Toto sdílení vyprší {expiration}.",
"upload.modal.completed.share-ready": "Sdílení připraveno",
"upload.modal.completed.notified-reverse-share-creator": "Upozornili jsme tvůrce zpětného sdílení. Můžete s nimi také ručně sdílet tento odkaz jiným způsobem.",
// END /upload
// /share/[id]
"share.title": "Sdílení {shareId}",
"share.description": "Podívejte se, co jsem s vámi sdílel!",
"share.error.visitor-limit-exceeded.title": "Limit návštěvníků překročen",
"share.error.visitor-limit-exceeded.description": "Limit návštěvníků tohoto sdílení byl překročen.",
"share.error.removed.title": "Sdílení bylo odstraněno",
"share.error.not-found.title": "Sdílení nenalezeno",
"share.error.not-found.description": "Sdílení, které hledáte, neexistuje.",
"share.error.access-denied.title": "Soukromé sdílení",
"share.error.access-denied.description": "Aktuální účet nemá oprávnění k přístupu k tomuto sdílení",
"share.modal.password.title": "Heslo vyžadováno",
"share.modal.password.description": "Please enter the password to acces this share.",
"share.modal.password": "Heslo",
"share.modal.error.invalid-password": "Neplatné heslo",
"share.button.download-all": "Stáhnout vše",
"share.notify.download-all-preparing": "The share is being prepared. Please try again in a few minutes.",
"share.modal.file-link": "Odkaz na soubor",
"share.table.name": "Název",
"share.table.size": "Velikost",
"share.modal.file-preview.error.not-supported.title": "Náhled není podporován",
"share.modal.file-preview.error.not-supported.description": "Previews are not supported for this type of files. Please download the file to view it.",
// END /share/[id]
// /share/[id]/edit
"share.edit.title": "Upravit {shareId}",
"share.edit.append-upload": "Připojit soubor",
"share.edit.notify.generic-error": "Při dokončování vašeho sdílení došlo k chybě.",
"share.edit.notify.save-success": "Sdílení úspěšně aktualizováno",
// END /share/[id]/edit
// /admin/config
"admin.config.title": "Nastavení",
"admin.config.category.general": "Obecné",
"admin.config.category.share": "Sdílení",
"admin.config.category.email": "E-mail",
"admin.config.category.smtp": "SMTP",
"admin.config.category.oauth": "Přihlášení přes sociální sítě",
"admin.config.general.app-name": "Název aplikace",
"admin.config.general.app-name.description": "Název aplikace",
"admin.config.general.app-url": "URL aplikace",
"admin.config.general.app-url.description": "Na kterém URL je Pingvin Share k dispozici",
"admin.config.general.secure-cookies": "Secure cookies",
"admin.config.general.secure-cookies.description": "Whether to set the secure flag on cookies. If enabled, the site will not function when accessed over HTTP.",
"admin.config.general.show-home-page": "Zobrazit domovskou stránku",
"admin.config.general.show-home-page.description": "Zda zobrazovat domovskou stránku",
"admin.config.general.session-duration": "Délka trvání relace",
"admin.config.general.session-duration.description": "Čas v hodinách, po kterém se uživatel musí znovu přihlásit (výchozí: 3 měsíce).",
"admin.config.general.logo": "Logo",
"admin.config.general.logo.description": "Změňte své logo nahráním nového obrázku. Obrázek musí být PNG a měl by mít formát 1:1.",
"admin.config.general.logo.placeholder": "Vybrat obrázek",
"admin.config.email.enable-share-email-recipients": "Enable email recipient sharing",
"admin.config.email.enable-share-email-recipients.description": "Whether to allow email sharing with recipients. Only enable this if SMTP is activated.",
"admin.config.email.share-recipients-subject": "Share recipients subject",
"admin.config.email.share-recipients-subject.description": "Subject of the email which gets sent to the share recipients.",
"admin.config.email.share-recipients-message": "Share recipients message",
"admin.config.email.share-recipients-message.description": "Message which gets sent to the share recipients. Available variables:\n {creator} - The username of the creator of the share\n {creatorEmail} - The email of the creator of the share\n {shareUrl} - The URL of the share\n {desc} - The description of the share\n {expires} - The expiration date of the share\n These variables will be replaced with the actual value.",
"admin.config.email.reverse-share-subject": "Předmět e-mailu o zpětném sdílení",
"admin.config.email.reverse-share-subject.description": "Subject of the sent email when someone created a share with your reverse share link.",
"admin.config.email.reverse-share-message": "Zpráva o zpětném sdílení",
"admin.config.email.reverse-share-message.description": "Zpráva, která bude odeslána, když někdo vytvoří sdílení s vaším odkazem na zpětné sdílení. {shareUrl} bude nahrazeno jménem tvůrce a URL pro sdílení.",
"admin.config.email.reset-password-subject": "Předmět e-mailu pro obnovení hesla",
"admin.config.email.reset-password-subject.description": "Subject of the sent email when a user requests a password reset.",
"admin.config.email.reset-password-message": "Zpráva o obnovení hesla",
"admin.config.email.reset-password-message.description": "Zpráva, která bude odeslána, když uživatel požádá o obnovení hesla. {url} bude nahrazeno URL pro obnovení hesla.",
"admin.config.email.invite-subject": "Předmět pozvánky",
"admin.config.email.invite-subject.description": "Subject of the sent email when an admin invites a user.",
"admin.config.email.invite-message": "Zpráva pozvánky",
"admin.config.email.invite-message.description": "Message which gets sent when an admin invites a user. {url} will be replaced with the invite URL, {email} with the email and {password} with the users password.",
"admin.config.share.allow-registration": "Povolit registraci",
"admin.config.share.allow-registration.description": "Zda je registrace povolena",
"admin.config.share.allow-unauthenticated-shares": "Povolit sdílení neověřeným uživatelům",
"admin.config.share.allow-unauthenticated-shares.description": "Zda mohou neověření uživatelé vytvářet sdílení",
"admin.config.share.max-expiration": "Max. platnost",
"admin.config.share.max-expiration.description": "Maximální platnost sdílení v hodinách. Nastavte na 0 k povolení neomezené platnosti.",
"admin.config.share.share-id-length": "Default share ID length",
"admin.config.share.share-id-length.description": "Default length for the generated ID of a share. This value is also used to generate links for reverse shares. A value below 8 is not considered secure.",
"admin.config.share.max-size": "Max. velikost",
"admin.config.share.max-size.description": "Maximální velikost sdílení v bajtech",
"admin.config.share.zip-compression-level": "Úroveň Zip komprese",
"admin.config.share.zip-compression-level.description": "Upravte úroveň pro rovnováhu mezi velikostí souboru a rychlostí komprese. Platné hodnoty se pohybují od 0 do 9, přičemž 0 znamená bez komprese a 9 je maximální komprese. ",
"admin.config.share.chunk-size": "Velikost bloku",
"admin.config.share.chunk-size.description": "Adjust the chunk size (in bytes) for your uploads to balance efficiency and reliability according to your internet connection. Smaller chunks can enhance success rates for unstable connections, while larger chunks make uploads faster for stable connections.",
"admin.config.share.auto-open-share-modal": "Automaticky otevřít menu vytvoření sdílení",
"admin.config.share.auto-open-share-modal.description": "Menu vytvoření sdílení se automaticky zobrazí, když uživatel vybere soubory, čímž se eliminuje potřeba ručně kliknout na tlačítko.",
"admin.config.smtp.enabled": "Enable",
"admin.config.smtp.enabled.description": "Zda je SMTP povoleno. Povolte pouze pokud jste zadali hostitele, port, e-mail, uživatele a heslo vašeho SMTP serveru.",
"admin.config.smtp.host": "Hostitel",
"admin.config.smtp.host.description": "Hostitel SMTP serveru",
"admin.config.smtp.port": "Port",
"admin.config.smtp.port.description": "Port SMTP serveru",
"admin.config.smtp.email": "E-mail",
"admin.config.smtp.email.description": "Email address from wich the emails get sent",
"admin.config.smtp.username": "Uživatelské jméno",
"admin.config.smtp.username.description": "Uživatelské jméno SMTP serveru",
"admin.config.smtp.password": "Heslo",
"admin.config.smtp.password.description": "Heslo SMTP serveru",
"admin.config.smtp.button.test": "Odeslat testovací e-mail",
"admin.config.smtp.allow-unauthorized-certificates": "Důvěřovat neoprávněným certifikátům SMTP serveru",
"admin.config.smtp.allow-unauthorized-certificates.description": "Povolte pouze pokud potřebujete důvěřovat vlastnoručně podepsaným certifikátům.",
"admin.config.oauth.allow-registration": "Povolit registraci",
"admin.config.oauth.allow-registration.description": "Povolit uživatelům registrovat se přes sociální sítě",
"admin.config.oauth.ignore-totp": "Ignorovat TOTP",
"admin.config.oauth.ignore-totp.description": "Ignorovat TOTP při přihlášení přes sociální sítě",
"admin.config.oauth.disable-password": "Zakázat přihlášení heslem",
"admin.config.oauth.disable-password.description": "Zda zakázat přihlášení heslem\nUjistěte se, že je OAuth poskytovatel správně nakonfigurován před aktivací této konfigurace, abyste se vyhnuli ztrátě přístupu.",
"admin.config.oauth.github-enabled": "GitHub",
"admin.config.oauth.github-enabled.description": "Zda je povoleno přihlášení přes GitHub",
"admin.config.oauth.github-client-id": "GitHub Client ID",
"admin.config.oauth.github-client-id.description": "Client ID GitHub OAuth aplikace",
"admin.config.oauth.github-client-secret": "GitHub Client secret",
"admin.config.oauth.github-client-secret.description": "Client secret of the GitHub OAuth app",
"admin.config.oauth.google-enabled": "Google",
"admin.config.oauth.google-enabled.description": "Zda je povoleno přihlášení přes Google",
"admin.config.oauth.google-client-id": "Google Client ID",
"admin.config.oauth.google-client-id.description": "Client ID of the Google OAuth app",
"admin.config.oauth.google-client-secret": "Google Client secret",
"admin.config.oauth.google-client-secret.description": "Client secret of the Google OAuth app",
"admin.config.oauth.microsoft-enabled": "Microsoft",
"admin.config.oauth.microsoft-enabled.description": "Zda je povoleno přihlášení přes Microsoft",
"admin.config.oauth.microsoft-tenant": "Microsoft Tenant",
"admin.config.oauth.microsoft-tenant.description": "Tenant ID of the Microsoft OAuth app\ncommon: Users with both a personal Microsoft account and a work or school account from Microsoft Entra ID can sign in to the application. organizations: Only users with work or school accounts from Microsoft Entra ID can sign in to the application.\nconsumers: Only users with a personal Microsoft account can sign in to the application.\ndomain name of the Microsoft Entra tenant or the tenant ID in GUID format: Only users from a specific Microsoft Entra tenant (directory members with a work or school account or directory guests with a personal Microsoft account) can sign in to the application.",
"admin.config.oauth.microsoft-client-id": "Microsoft Client ID",
"admin.config.oauth.microsoft-client-id.description": "Client ID of the Microsoft OAuth app",
"admin.config.oauth.microsoft-client-secret": "Microsoft Client secret",
"admin.config.oauth.microsoft-client-secret.description": "Client secret of the Microsoft OAuth app",
"admin.config.oauth.discord-enabled": "Discord",
"admin.config.oauth.discord-enabled.description": "Zda je povoleno přihlášení přes Discord",
"admin.config.oauth.discord-limited-users": "Discord limited users",
"admin.config.oauth.discord-limited-users.description": "Limit signing in to specific users by their Discord ID. Leave it blank to disable.",
"admin.config.oauth.discord-limited-guild": "Discord limited server ID",
"admin.config.oauth.discord-limited-guild.description": "Omezit přihlášení na uživatele na konkrétním serveru. Ponechte prázdné pro vypnutí.",
"admin.config.oauth.discord-client-id": "Discord Client ID",
"admin.config.oauth.discord-client-id.description": "Client ID of the Discord OAuth app",
"admin.config.oauth.discord-client-secret": "Discord Client secret",
"admin.config.oauth.discord-client-secret.description": "Client secret of the Discord OAuth app",
"admin.config.oauth.oidc-enabled": "OpenID Connect",
"admin.config.oauth.oidc-enabled.description": "Zda je povoleno přihlášení přes OpenID Connect",
"admin.config.oauth.oidc-discovery-uri": "OpenID Connect Discovery URI",
"admin.config.oauth.oidc-discovery-uri.description": "Discovery URI of the OpenID Connect OAuth app",
"admin.config.oauth.oidc-sign-out": "Sign out from OpenID Connect",
"admin.config.oauth.oidc-sign-out.description": "Whether the “Sign out” button will sign out from the OpenID Connect provider",
"admin.config.oauth.oidc-scope": "OpenID Connect scope",
"admin.config.oauth.oidc-scope.description": "Scopes which should be requested from the OpenID Connect provider.",
"admin.config.oauth.oidc-username-claim": "OpenID Connect username claim",
"admin.config.oauth.oidc-username-claim.description": "Username claim in OpenID Connect ID token. Leave it blank if you don't know what this config is.",
"admin.config.oauth.oidc-role-path": "Path to roles in OpenID Connect token",
"admin.config.oauth.oidc-role-path.description": "Musí být platná JMES cesta odkazující na pole rolí. " + "Správa přístupových práv pomocí OpenID Connect rolí je doporučena pouze v případě, že není nastaven žádný jiný poskytovatel identity a přihlášení heslem je zakázáno. " + "Ponechte prázdné, pokud nevíte, co tato konfigurace znamená.",
"admin.config.oauth.oidc-role-general-access": "OpenID Connect role pro obecný přístup",
"admin.config.oauth.oidc-role-general-access.description": "Role required for general access. Must be present in a users roles for them to log in. " + "Ponechte prázdné, pokud nevíte, co tato konfigurace znamená.",
"admin.config.oauth.oidc-role-admin-access": "OpenID Connect role for admin access",
"admin.config.oauth.oidc-role-admin-access.description": "Role required for administrative access. Must be present in a users roles for them to access the admin panel. " + "Ponechte prázdné, pokud nevíte, co tato konfigurace znamená.",
"admin.config.oauth.oidc-client-id": "OpenID Connect Client ID",
"admin.config.oauth.oidc-client-id.description": "Client ID of the OpenID Connect OAuth app",
"admin.config.oauth.oidc-client-secret": "OpenID Connect Client secret",
"admin.config.oauth.oidc-client-secret.description": "Client secret of the OpenID Connect OAuth app",
"admin.config.category.ldap": "LDAP",
"admin.config.ldap.enabled": "Enable LDAP",
"admin.config.ldap.enabled.description": "Use LDAP authentication for user login",
"admin.config.ldap.url": "URL serveru",
"admin.config.ldap.url.description": "URL of the LDAP server",
"admin.config.ldap.bind-dn": "Bind DN",
"admin.config.ldap.bind-dn.description": "Default user used to perform the user search",
"admin.config.ldap.bind-password": "Bind password",
"admin.config.ldap.bind-password.description": "Password used to perform the user search",
"admin.config.ldap.search-base": "User base",
"admin.config.ldap.search-base.description": "Základní umístění, kde budou prováděna hledání uživatelů",
"admin.config.ldap.search-query": "User query",
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
"admin.config.ldap.admin-groups": "Admin group",
"admin.config.ldap.admin-groups.description": "Skupina potřebná pro administrativní přístup.",
"admin.config.ldap.field-name-member-of": "User groups attribute name",
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
"admin.config.ldap.field-name-email": "User email attribute name",
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
// 404
"404.description": "Jejda, tato stránka neexistuje.",
"404.button.home": "Bring me back home",
// error
"error.title": "Chyba",
"error.description": "Jejda!",
"error.button.back": "Vrátit se zpět",
"error.msg.default": "Něco se pokazilo.",
"error.msg.access_denied": "Zrušili jste proces ověřování, zkuste to prosím znovu.",
"error.msg.expired_token": "Proces ověřování trval příliš dlouho, zkuste to prosím znovu.",
"error.msg.invalid_token": "Interní chyba",
"error.msg.no_user": "Uživatel propojený s tímto účtem {0} neexistuje.",
"error.msg.no_email": "Z tohoto účtu {0} nelze získat e-mailovou adresu.",
"error.msg.already_linked": "Tento účet {0} je již propojen s jiným účtem.",
"error.msg.not_linked": "This {0} account hasn't been linked to any account yet.",
"error.msg.unverified_account": "Tento účet {0} není ověřen, zkuste to prosím znovu po ověření.",
"error.msg.user_not_allowed": "Nemáte oprávnění k přihlášení.",
"error.msg.cannot_get_user_info": "Cannot get your user info from this {0} account.",
"error.param.provider_github": "GitHub",
"error.param.provider_google": "Google",
"error.param.provider_microsoft": "Microsoft",
"error.param.provider_discord": "Discord",
"error.param.provider_oidc": "OpenID Connect",
// Common translations
"common.button.save": "Uložit",
"common.button.create": "Vytvořit",
"common.button.submit": "Odeslat",
"common.button.delete": "Smazat",
"common.button.cancel": "Zrušit",
"common.button.confirm": "Potvrdit",
"common.button.disable": "Zakázat",
"common.button.share": "Sdílet",
"common.button.generate": "Generovat",
"common.button.done": "Hotovo",
"common.text.link": "Odkaz",
"common.text.navigate-to-link": "Visit link",
"common.text.or": "nebo",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "Vrátit se zpět",
"common.button.go-home": "Jít domů",
"common.notify.copied": "Váš odkaz byl zkopírován do schránky",
"common.success": "Úspěch",
"common.error": "Chyba",
"common.error.unknown": "Došlo k neznámé chybě",
"common.error.invalid-email": "Invalid email address",
"common.error.too-short": "Musí mít alespoň {length} znaků",
"common.error.too-long": "Musí mít maximálně {length} znaků",
"common.error.number-too-small": "Must be at least {min}",
"common.error.number-too-large": "Must be at most {max}",
"common.error.exact-length": "Musí mít přesně {length} znaků",
"common.error.invalid-number": "Musí být číslo",
"common.error.field-required": "Toto pole je povinné"
};

View File

@@ -3,7 +3,7 @@ export default {
"navbar.upload": "Upload",
"navbar.signin": "Log ind",
"navbar.home": "Hjem",
"navbar.signup": "Opret bruger",
"navbar.signup": "Sign up",
"navbar.links.shares": "Mine delte filer",
"navbar.links.reverse": "Omvendt deling",
"navbar.avatar.account": "Min bruger",
@@ -16,9 +16,9 @@ export default {
"home.bullet.a.name": "Self-Hosted",
"home.bullet.a.description": "Host Pingvin Share på din egen maskine.",
"home.bullet.b.name": "Privatliv",
"home.bullet.b.description": "Dine filer er dine filer og bør ikke komme i hænderne på tredjeparter.",
"home.bullet.b.description": "Your files are yours and will never be accessed by third parties.",
"home.bullet.c.name": "Ingen irriterende grænse for filstørrelse",
"home.bullet.c.description": "Upload så store filer, som du vil. Kun din harddisk sætter grænsen.",
"home.bullet.c.description": "Upload files as big as you want. Only your hard drive will be your limit.",
"home.button.start": "Kom i gang",
"home.button.source": "Source code",
// END /
@@ -34,6 +34,7 @@ export default {
"signIn.notify.totp-required.title": "2-faktor login påkrævet",
"signIn.notify.totp-required.description": "Indtast den aktuelle engangskode fra din 2-faktor Authenticator",
"signIn.oauth.or": "OR",
"signIn.oauth.signInWith": "Log ind med",
"signIn.oauth.github": "GitHub",
"signIn.oauth.google": "Google",
"signIn.oauth.microsoft": "Microsoft",
@@ -57,12 +58,12 @@ export default {
// /auth/reset-password
"resetPassword.title": "Glemt din adgangskode?",
"resetPassword.description": "Indtast din e-mail for at nulstille din adgangskode.",
"resetPassword.notify.success": "A message with a link to reset your password has been sent if the email exists.",
"resetPassword.notify.success": "A message with a link to reset your password has been sent if the provided email exists.",
"resetPassword.button.back": "Tilbage til login",
"resetPassword.text.resetPassword": "Nulstil adgangskode",
"resetPassword.text.enterNewPassword": "Indtast din nye adgangskode",
"resetPassword.input.password": "Ny adgangskode",
"resetPassword.notify.passwordReset": "Adgangskoden er blevet nulstillet.",
"resetPassword.notify.passwordReset": "Your password has been successfully reset.",
// /account
"account.title": "Min bruger",
"account.card.info.title": "Brugerinfo",
@@ -72,7 +73,7 @@ export default {
"account.card.password.title": "Adgangskode",
"account.card.password.old": "Gammel adgangskode",
"account.card.password.new": "Ny adgangskode",
"account.card.password.noPasswordSet": "You don't have a password set. If you want to sign in with email and password you need to set a password.",
"account.card.password.noPasswordSet": "You do not have a password set. To sign in using your email and password, you need to create a password.",
"account.notify.password.success": "Adgangskoden er ændret",
"account.card.oauth.title": "Social login",
"account.card.oauth.github": "GitHub",
@@ -84,7 +85,7 @@ export default {
"account.card.oauth.unlink": "Unlink",
"account.card.oauth.unlinked": "Unlinked",
"account.modal.unlink.title": "Unlink account",
"account.modal.unlink.description": "Unlinking your social accounts may cause you to lose your account if you don't remember your username and password.",
"account.modal.unlink.description": "Unlinking your social accounts may cause you to lose your account if you don't remember your login credentials",
"account.notify.oauth.unlinked.success": "Unlinked successfully",
"account.card.security.title": "Sikkerhed",
"account.card.security.totp.enable.description": "Indtast din nuværende adgangskode for at begynde opsætningen af 2-faktor login",
@@ -120,12 +121,12 @@ export default {
"account.shares.table.name": "Navn",
"account.shares.table.description": "Beskrivelse",
"account.shares.table.visitors": "Besøgende",
"account.shares.table.expiresAt": "Udløber d",
"account.shares.table.createdAt": "Oprettet d.",
"account.shares.table.expiresAt": "Expires on",
"account.shares.table.createdAt": "Created on",
"account.shares.table.size": "Størrelse",
"account.shares.modal.share-informations": "Share informations",
"account.shares.modal.share-link": "Del link",
"account.shares.modal.delete.title": "Slet share {share}",
"account.shares.modal.delete.title": "Delete share: {share}",
"account.shares.modal.delete.description": "Ønsker du virkelig at slette denne deling?",
// END /account/shares
// /account/reverseShares
@@ -134,7 +135,7 @@ export default {
"account.reverseShares.title.empty": "Der er tomt her 👀",
"account.reverseShares.description.empty": "You don't have any reverse shares.",
// showCreateReverseShareModal.tsx
"account.reverseShares.modal.title": "Create reverse share",
"account.reverseShares.modal.title": "Opret omvendt deling",
"account.reverseShares.modal.expiration.label": "Udløb",
"account.reverseShares.modal.expiration.minute-singular": "Minut",
"account.reverseShares.modal.expiration.minute-plural": "Minutter",
@@ -149,8 +150,12 @@ export default {
"account.reverseShares.modal.expiration.year-singular": "År",
"account.reverseShares.modal.expiration.year-plural": "År",
"account.reverseShares.modal.max-size.label": "Maksimal størrelse for deling",
"account.reverseShares.modal.send-email": "Send e-mail notifikation",
"account.reverseShares.modal.send-email.description": "Send en e-mail notifikation, når der oprettes en deling med dette omvendte delingslink.",
"account.reverseShares.modal.send-email": "Send email notifications",
"account.reverseShares.modal.send-email.description": "Sends you an email notification when a share is created with this reverse share link.",
"account.reverseShares.modal.simplified": "Simple mode",
"account.reverseShares.modal.simplified.description": "Make it easy for the person uploading the file to share it with you. They will only be able to customize the name and description of the share.",
"account.reverseShares.modal.public-access": "Offentlig adgang",
"account.reverseShares.modal.public-access.description": "Make the shares created with this reverse share public. If disabled, only you and the share creator will have access to view it.",
"account.reverseShares.modal.max-use.label": "Maksimal anvendelser",
"account.reverseShares.modal.max-use.description": "Det maksimale antal gange, denne URL kan bruges til at oprette en deling.",
"account.reverseShare.never-expires": "Denne omvendte deling udløber aldrig.",
@@ -163,7 +168,7 @@ export default {
"account.reverseShares.table.max-size": "Maksimal størrelse for deling",
"account.reverseShares.table.expires": "Udløber d",
"account.reverseShares.modal.reverse-share-link": "Reverse share link",
"account.reverseShares.modal.delete.title": "Delete reverse share",
"account.reverseShares.modal.delete.title": "Slet omvendt deling",
"account.reverseShares.modal.delete.description": "Ønsker du virkelig at slette denne omvendte deling? Hvis du gør det, vil de tilknyttede delinger også blive slettet.",
// END /account/reverseShares
// /admin
@@ -178,14 +183,14 @@ export default {
"admin.users.table.username": "Brugernavn",
"admin.users.table.email": "E-mail",
"admin.users.table.admin": "Admin",
"admin.users.edit.update.title": "Opdater bruger {username}",
"admin.users.edit.update.title": "Edit user: {username}",
"admin.users.edit.update.admin-privileges": "Admin rettigheder",
"admin.users.edit.update.change-password.title": "Skift adgangskode",
"admin.users.edit.update.change-password.field": "Ny adgangskode",
"admin.users.edit.update.change-password.button": "Gem ny adgangskode",
"admin.users.edit.update.notify.password.success": "Adgangskoden er ændret",
"admin.users.edit.delete.title": "Slet bruger {username}",
"admin.users.edit.delete.description": "Er du sikker på du vil slette denne bruger og tilhørende delinger?",
"admin.users.edit.delete.title": "Delete user: {username} ?",
"admin.users.edit.delete.description": "Do you really want to delete this user and all their shares?",
// showCreateUserModal.tsx
"admin.users.modal.create.title": "Opret bruger",
"admin.users.modal.create.username": "Brugernavn",
@@ -201,17 +206,20 @@ export default {
"admin.shares.table.id": "Share ID",
"admin.shares.table.username": "Creator",
"admin.shares.table.visitors": "Besøgende",
"admin.shares.table.expires": "Expires At",
"admin.shares.edit.delete.title": "Delete share {id}",
"admin.shares.table.expires": "Expires on",
"admin.shares.edit.delete.title": "Delete share: {id}",
"admin.shares.edit.delete.description": "Do you really want to delete this share?",
// END /admin/shares
// /upload
"upload.title": "Upload",
"upload.notify.confirm-leave": "Are you sure you want to leave this page? Your upload will be canceled.",
"upload.notify.generic-error": "Der opstod en fejl under afslutningen af din deling.",
"upload.notify.count-failed": "{count} files failed to upload. Trying again.",
"upload.reverse-share.error.invalid.title": "Invalid reverse share link",
"upload.reverse-share.error.invalid.description": "This reverse share has expired or is invalid.",
// Dropzone.tsx
"upload.dropzone.title": "Upload filer",
"upload.dropzone.description": "Drag'n'drop files here to start your share. We can accept only files that are less than {maxSize} in total.",
"upload.dropzone.description": "Drag'n'drop files here to start your share. We only accept files up to {maxSize} in total.",
"upload.dropzone.notify.file-too-big": "Your files exceed the maximum share size of {maxSize}.",
// FileList.tsx
"upload.filelist.name": "Navn",
@@ -223,8 +231,8 @@ export default {
"upload.modal.not-signed-in": "Du er ikke logget ind",
"upload.modal.not-signed-in-description": "Du vil ikke være i stand til at slette din deling manuelt og se antallet af besøgende.",
"upload.modal.expires.never": "aldrig",
"upload.modal.expires.never-long": "Udløber aldrig",
"upload.modal.expires.error.too-long": "Udløbsdatoen overskrider den maksimalt tilladte udløbsdato på {max}.",
"upload.modal.expires.never-long": "Permanent share",
"upload.modal.expires.error.too-long": "Expiration date exceeds the maximum of {max}.",
"upload.modal.link.label": "Link",
"upload.modal.expires.label": "Udløb",
"upload.modal.expires.minute-singular": "Minut",
@@ -239,7 +247,7 @@ export default {
"upload.modal.expires.month-plural": "Måneder",
"upload.modal.expires.year-singular": "År",
"upload.modal.expires.year-plural": "År",
"upload.modal.accordion.name-and-description.title": "Name and description",
"upload.modal.accordion.name-and-description.title": "Navn og beskrivelse",
"upload.modal.accordion.name-and-description.name.placeholder": "Navn",
"upload.modal.accordion.name-and-description.description.placeholder": "Note for the recipients of this share",
"upload.modal.accordion.email.title": "E-mail modtagere",
@@ -254,6 +262,7 @@ export default {
"upload.modal.completed.never-expires": "Denne deling vil aldrig udløbe.",
"upload.modal.completed.expires-on": "Denne omvendte deling udløber den {expiration}.",
"upload.modal.completed.share-ready": "Delingen er klar",
"upload.modal.completed.notified-reverse-share-creator": "We have notified the creator of the reverse share. You can also manually share this link with them through other means.",
// END /upload
// /share/[id]
"share.title": "Del {shareId}",
@@ -263,23 +272,25 @@ export default {
"share.error.removed.title": "Deling fjernet",
"share.error.not-found.title": "Delingen blev ikke fundet",
"share.error.not-found.description": "Den deling, du leder efter, eksisterer ikke.",
"share.error.access-denied.title": "Privat deling",
"share.error.access-denied.description": "The current account does not have permission to access this share",
"share.modal.password.title": "Adgangskode påkrævet",
"share.modal.password.description": "For at få adgang til denne deling, indtast venligst adgangskoden til delingen.",
"share.modal.password.description": "Please enter the password to acces this share.",
"share.modal.password": "Adgangskode",
"share.modal.error.invalid-password": "Ugyldig adgangskode",
"share.button.download-all": "Download alle",
"share.notify.download-all-preparing": "Delingen forberedes. Prøv igen om et par minutter.",
"share.notify.download-all-preparing": "The share is being prepared. Please try again in a few minutes.",
"share.modal.file-link": "Fil link",
"share.table.name": "Navn",
"share.table.size": "Størrelse",
"share.modal.file-preview.error.not-supported.title": "Forhåndsvisning ikke understøttet",
"share.modal.file-preview.error.not-supported.description": "A preview for this file type is unsupported. Please download the file to view it.",
"share.modal.file-preview.error.not-supported.description": "Previews are not supported for this type of files. Please download the file to view it.",
// END /share/[id]
// /share/[id]/edit
"share.edit.title": "Rediger {shareId}",
"share.edit.append-upload": "Append file",
"share.edit.notify.generic-error": "An error occurred while finishing your share.",
"share.edit.notify.save-success": "Share updated successfully",
"share.edit.notify.save-success": "Deling opdateret",
// END /share/[id]/edit
// /admin/config
"admin.config.title": "Konfiguration",
@@ -292,6 +303,8 @@ export default {
"admin.config.general.app-name.description": "Navnet på applikationen",
"admin.config.general.app-url": "App URL",
"admin.config.general.app-url.description": "På hvilken URL Pingvin Share er tilgængelig",
"admin.config.general.secure-cookies": "Secure cookies",
"admin.config.general.secure-cookies.description": "Whether to set the secure flag on cookies. If enabled, the site will not function when accessed over HTTP.",
"admin.config.general.show-home-page": "Vis forside",
"admin.config.general.show-home-page.description": "Om forsiden skal vises",
"admin.config.general.session-duration": "Session Duration",
@@ -299,57 +312,63 @@ export default {
"admin.config.general.logo": "Logo",
"admin.config.general.logo.description": "Skift dit logo ved at uploade et nyt billede. Billedet skal være PNG og skal have formatet 1:1.",
"admin.config.general.logo.placeholder": "Vælg billede",
"admin.config.email.enable-share-email-recipients": "Aktiver deling til e-mail modtagere",
"admin.config.email.enable-share-email-recipients.description": "Whether to allow emails to share recipients. Only enable this if you have enabled SMTP.",
"admin.config.email.enable-share-email-recipients": "Enable email recipient sharing",
"admin.config.email.enable-share-email-recipients.description": "Whether to allow email sharing with recipients. Only enable this if SMTP is activated.",
"admin.config.email.share-recipients-subject": "Share recipients subject",
"admin.config.email.share-recipients-subject.description": "Subject of the email which gets sent to the share recipients.",
"admin.config.email.share-recipients-message": "Share recipients message",
"admin.config.email.share-recipients-message.description": "Message which gets sent to the share recipients. Available variables:\n {creator} - The username of the creator of the share\n {shareUrl} - The URL of the share\n {desc} - The description of the share\n {expires} - The expiration date of the share\n The variables will be replaced with the actual value.",
"admin.config.email.share-recipients-message.description": "Message which gets sent to the share recipients. Available variables:\n {creator} - The username of the creator of the share\n {creatorEmail} - The email of the creator of the share\n {shareUrl} - The URL of the share\n {desc} - The description of the share\n {expires} - The expiration date of the share\n These variables will be replaced with the actual value.",
"admin.config.email.reverse-share-subject": "Reverse share subject",
"admin.config.email.reverse-share-subject.description": "Subject of the email which gets sent when someone created a share with your reverse share link.",
"admin.config.email.reverse-share-subject.description": "Subject of the sent email when someone created a share with your reverse share link.",
"admin.config.email.reverse-share-message": "Reverse share message",
"admin.config.email.reverse-share-message.description": "Message which gets sent when someone created a share with your reverse share link. {shareUrl} will be replaced with the creator's name and the share URL.",
"admin.config.email.reset-password-subject": "Reset password subject",
"admin.config.email.reset-password-subject.description": "Subject of the email which gets sent when a user requests a password reset.",
"admin.config.email.reset-password-subject.description": "Subject of the sent email when a user requests a password reset.",
"admin.config.email.reset-password-message": "Nulstil adgangskode besked",
"admin.config.email.reset-password-message.description": "Message which gets sent when a user requests a password reset. {url} will be replaced with the reset password URL.",
"admin.config.email.invite-subject": "Invitations emne",
"admin.config.email.invite-subject.description": "Emne for den e-mail, der sendes, når en administrator inviterer en ny bruger.",
"admin.config.email.invite-subject.description": "Subject of the sent email when an admin invites a user.",
"admin.config.email.invite-message": "Invitations besked",
"admin.config.email.invite-message.description": "Besked som bliver sendt, når en administrator inviterer en bruger. {url} vil blive erstattet med invitations-URL'en og {password} med adgangskoden.",
"admin.config.email.invite-message.description": "Message which gets sent when an admin invites a user. {url} will be replaced with the invite URL, {email} with the email and {password} with the users password.",
"admin.config.share.allow-registration": "Tillad oprettelser",
"admin.config.share.allow-registration.description": "Om alle skal kunne oprette en bruger",
"admin.config.share.allow-unauthenticated-shares": "Tillad uautoriserede delinger",
"admin.config.share.allow-unauthenticated-shares.description": "Whether unauthenticated users can create shares",
"admin.config.share.max-expiration": "Maks. udløb",
"admin.config.share.max-expiration.description": "Maximum share expiration in hours. Set to 0 to allow unlimited expiration.",
"admin.config.share.share-id-length": "Default share ID length",
"admin.config.share.share-id-length.description": "Default length for the generated ID of a share. This value is also used to generate links for reverse shares. A value below 8 is not considered secure.",
"admin.config.share.max-size": "Maks. størrelse",
"admin.config.share.max-size.description": "Maksimal filstørrelse i bytes",
"admin.config.share.zip-compression-level": "Zip compression level",
"admin.config.share.zip-compression-level.description": "Adjust the level to balance between file size and compression speed. Valid values range from 0 to 9, with 0 being no compression and 9 being maximum compression. ",
"admin.config.share.chunk-size": "Chunk size",
"admin.config.share.chunk-size.description": "Adjust the chunk size (in bytes) for your uploads to balance efficiency and reliability according to your internet connection. Smaller chunks can enhance success rates for unstable connections, while larger chunks speed up uploads for stable connections.",
"admin.config.share.chunk-size.description": "Adjust the chunk size (in bytes) for your uploads to balance efficiency and reliability according to your internet connection. Smaller chunks can enhance success rates for unstable connections, while larger chunks make uploads faster for stable connections.",
"admin.config.share.auto-open-share-modal": "Auto open create share modal",
"admin.config.share.auto-open-share-modal.description": "The share creation modal automatically appears when a user selects files, eliminating the need to manually click the button.",
"admin.config.smtp.enabled": "Aktiveret",
"admin.config.smtp.enabled": "Enable",
"admin.config.smtp.enabled.description": "Om SMTP er aktiveret. Aktiver kun SMTP, hvis du har indtastet SMTP-server vært, port, e-mail, bruger og adgangskode.",
"admin.config.smtp.host": "Vært",
"admin.config.smtp.host.description": "Vært for SMTP serveren",
"admin.config.smtp.port": "Port",
"admin.config.smtp.port.description": "Porten til SMTP serveren",
"admin.config.smtp.email": "E-mail",
"admin.config.smtp.email.description": "E-mail adressen som der skal afsendes fra",
"admin.config.smtp.email.description": "Email address from wich the emails get sent",
"admin.config.smtp.username": "Brugernavn",
"admin.config.smtp.username.description": "Brugernavnet til SMTP serveren",
"admin.config.smtp.password": "Adgangskode",
"admin.config.smtp.password.description": "Adgangskoden til SMTP serveren",
"admin.config.smtp.button.test": "Send test e-mail",
"admin.config.smtp.allow-unauthorized-certificates": "Trust unauthorized SMTP server certificates",
"admin.config.smtp.allow-unauthorized-certificates.description": "Only set this to true if you need to trust self signed certificates.",
"admin.config.oauth.allow-registration": "Tillad registrering",
"admin.config.oauth.allow-registration.description": "Allow users to register via social login",
"admin.config.oauth.allow-registration.description": "Tillad brugere at registrere sig via socialt login",
"admin.config.oauth.ignore-totp": "Ignore TOTP",
"admin.config.oauth.ignore-totp.description": "Whether to ignore TOTP when user using social login",
"admin.config.oauth.disable-password": "Deaktiver login med password",
"admin.config.oauth.disable-password.description": "Whether to disable password login\nMake sure that an OAuth provider is properly configured before activating this configuration to avoid being locked out.",
"admin.config.oauth.github-enabled": "GitHub",
"admin.config.oauth.github-enabled.description": "Whether GitHub login is enabled",
"admin.config.oauth.github-enabled.description": "Om GitHub login er aktiveret",
"admin.config.oauth.github-client-id": "GitHub Client ID",
"admin.config.oauth.github-client-id.description": "Client ID of the GitHub OAuth app",
"admin.config.oauth.github-client-secret": "GitHub Client secret",
@@ -370,6 +389,8 @@ export default {
"admin.config.oauth.microsoft-client-secret.description": "Client secret of the Microsoft OAuth app",
"admin.config.oauth.discord-enabled": "Discord",
"admin.config.oauth.discord-enabled.description": "Whether Discord login is enabled",
"admin.config.oauth.discord-limited-users": "Discord limited users",
"admin.config.oauth.discord-limited-users.description": "Limit signing in to specific users by their Discord ID. Leave it blank to disable.",
"admin.config.oauth.discord-limited-guild": "Discord limited server ID",
"admin.config.oauth.discord-limited-guild.description": "Limit signing in to users in a specific server. Leave it blank to disable.",
"admin.config.oauth.discord-client-id": "Discord Client ID",
@@ -380,12 +401,41 @@ export default {
"admin.config.oauth.oidc-enabled.description": "Whether OpenID Connect login is enabled",
"admin.config.oauth.oidc-discovery-uri": "OpenID Connect Discovery URI",
"admin.config.oauth.oidc-discovery-uri.description": "Discovery URI of the OpenID Connect OAuth app",
"admin.config.oauth.oidc-sign-out": "Sign out from OpenID Connect",
"admin.config.oauth.oidc-sign-out.description": "Whether the “Sign out” button will sign out from the OpenID Connect provider",
"admin.config.oauth.oidc-scope": "OpenID Connect scope",
"admin.config.oauth.oidc-scope.description": "Scopes which should be requested from the OpenID Connect provider.",
"admin.config.oauth.oidc-username-claim": "OpenID Connect username claim",
"admin.config.oauth.oidc-username-claim.description": "Username claim in OpenID Connect ID token. Leave it blank if you don't know what this config is.",
"admin.config.oauth.oidc-role-path": "Path to roles in OpenID Connect token",
"admin.config.oauth.oidc-role-path.description": "Must be a valid JMES path referencing an array of roles. " + "Managing access rights using OpenID Connect roles is only recommended if no other identity provider is configured and password login is disabled. " + "Leave it blank if you don't know what this config is.",
"admin.config.oauth.oidc-role-general-access": "OpenID Connect role for general access",
"admin.config.oauth.oidc-role-general-access.description": "Role required for general access. Must be present in a users roles for them to log in. " + "Leave it blank if you don't know what this config is.",
"admin.config.oauth.oidc-role-admin-access": "OpenID Connect role for admin access",
"admin.config.oauth.oidc-role-admin-access.description": "Role required for administrative access. Must be present in a users roles for them to access the admin panel. " + "Leave it blank if you don't know what this config is.",
"admin.config.oauth.oidc-client-id": "OpenID Connect Client ID",
"admin.config.oauth.oidc-client-id.description": "Client ID of the OpenID Connect OAuth app",
"admin.config.oauth.oidc-client-secret": "OpenID Connect Client secret",
"admin.config.oauth.oidc-client-secret.description": "Client secret of the OpenID Connect OAuth app",
"admin.config.category.ldap": "LDAP",
"admin.config.ldap.enabled": "Enable LDAP",
"admin.config.ldap.enabled.description": "Use LDAP authentication for user login",
"admin.config.ldap.url": "Server URL",
"admin.config.ldap.url.description": "URL of the LDAP server",
"admin.config.ldap.bind-dn": "Bind DN",
"admin.config.ldap.bind-dn.description": "Default user used to perform the user search",
"admin.config.ldap.bind-password": "Bind password",
"admin.config.ldap.bind-password.description": "Password used to perform the user search",
"admin.config.ldap.search-base": "User base",
"admin.config.ldap.search-base.description": "Base location, where the user search will be performed",
"admin.config.ldap.search-query": "User query",
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
"admin.config.ldap.admin-groups": "Admin gruppe",
"admin.config.ldap.admin-groups.description": "Gruppe påkrævet for administrativ adgang.",
"admin.config.ldap.field-name-member-of": "User groups attribute name",
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
"admin.config.ldap.field-name-email": "User email attribute name",
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
// 404
"404.description": "Ups! Denne side findes ikke.",
"404.button.home": "Gå tilbage",
@@ -393,17 +443,17 @@ export default {
"error.title": "Fejl",
"error.description": "Hovsa!",
"error.button.back": "Gå tilbage",
"error.msg.default": "Something went wrong.",
"error.msg.default": "Noget gik galt.",
"error.msg.access_denied": "You canceled the authentication process, please try again.",
"error.msg.expired_token": "The authentication process took too long, please try again.",
"error.msg.invalid_token": "Intern Fejl",
"error.msg.no_user": "User linked to this {0} account doesn't exist.",
"error.msg.no_email": "Can't get email address from this {0} account.",
"error.msg.already_linked": "This {0} account is already linked to another account.",
"error.msg.not_linked": "This {0} account haven't linked to any account yet.",
"error.msg.not_linked": "This {0} account hasn't been linked to any account yet.",
"error.msg.unverified_account": "This {0} account is unverified, please try again after verification.",
"error.msg.discord_guild_permission_denied": "Du har ikke tilladelse til at logge ind.",
"error.msg.cannot_get_user_info": "Can not get your user info from this {0} account.",
"error.msg.user_not_allowed": "Du har ikke tilladelse til at logge ind.",
"error.msg.cannot_get_user_info": "Cannot get your user info from this {0} account.",
"error.param.provider_github": "GitHub",
"error.param.provider_google": "Google",
"error.param.provider_microsoft": "Microsoft",
@@ -421,8 +471,9 @@ export default {
"common.button.generate": "Generer",
"common.button.done": "Færdig",
"common.text.link": "Link",
"common.text.navigate-to-link": "Go to the link",
"common.text.navigate-to-link": "Visit link",
"common.text.or": "eller",
"common.text.redirecting": "Omdirigerer...",
"common.button.go-back": "Gå tilbage",
"common.button.go-home": "Go home",
"common.notify.copied": "Linket blev kopieret til udklipsholderen",
@@ -432,6 +483,8 @@ export default {
"common.error.invalid-email": "Ugyldig e-mail",
"common.error.too-short": "Skal være på mindst {length} tegn",
"common.error.too-long": "Må højst være {length} tegn",
"common.error.number-too-small": "Must be at least {min}",
"common.error.number-too-large": "Must be at most {max}",
"common.error.exact-length": "Skal være præcis {length} tegn",
"common.error.invalid-number": "Skal være et tal",
"common.error.field-required": "Dette felt er påkrævet"

View File

@@ -16,9 +16,9 @@ export default {
"home.bullet.a.name": "Selbst gehostet",
"home.bullet.a.description": "Betreibe Pingvin Share auf deinem eigenen Server.",
"home.bullet.b.name": "Privatsphäre",
"home.bullet.b.description": "Deine Dateien gehören dir und sollten niemals in die Hände Dritter gelangen.",
"home.bullet.b.description": "Deine Dateien gehören dir und sind niemals durch Dritte zugänglich.",
"home.bullet.c.name": "Keine lästige Dateigrößenbegrenzung",
"home.bullet.c.description": "Lade Dateien beliebiger Größe hoch. Nur dein Festplattenspeicher stellt die Grenze dar.",
"home.bullet.c.description": "Lade so große Dateien hoch, wie du willst. Nur deine Festplatte ist dein Limit.",
"home.button.start": "Lege los",
"home.button.source": "Quellcode",
// END /
@@ -26,14 +26,15 @@ export default {
"signin.title": "Willkommen zurück",
"signin.description": "Du hast noch kein Konto?",
"signin.button.signup": "Registrieren",
"signin.input.email-or-username": "Email oder Benutzername",
"signin.input.email-or-username.placeholder": "Deine Email Adresse oder Benutzername",
"signin.input.email-or-username": "E-Mail oder Benutzername",
"signin.input.email-or-username.placeholder": "Deine E-Mail-Adresse oder Benutzername",
"signin.input.password": "Passwort",
"signin.input.password.placeholder": "Dein Passwort",
"signin.button.submit": "Anmelden",
"signIn.notify.totp-required.title": "Zwei-Faktor-Authentifizierung benötigt",
"signIn.notify.totp-required.description": "Bitte füge deinen Zwei-Faktor-Authentifizierungscode ein",
"signIn.notify.totp-required.description": "Bitte gib deinen Zwei-Faktor-Authentifizierungscode ein",
"signIn.oauth.or": "ODER",
"signIn.oauth.signInWith": "Anmelden mit",
"signIn.oauth.github": "GitHub",
"signIn.oauth.google": "Google",
"signIn.oauth.microsoft": "Microsoft",
@@ -46,8 +47,8 @@ export default {
"signup.button.signin": "Anmelden",
"signup.input.username": "Benutzername",
"signup.input.username.placeholder": "Dein Benutzername",
"signup.input.email": "Email",
"signup.input.email.placeholder": "Deine Emailadresse",
"signup.input.email": "E-Mail",
"signup.input.email.placeholder": "Deine E-Mail-Adresse",
"signup.button.submit": "Lass uns loslegen",
// END /auth/signup
// /auth/totp
@@ -56,7 +57,7 @@ export default {
// END /auth/totp
// /auth/reset-password
"resetPassword.title": "Passwort vergessen?",
"resetPassword.description": "Gib deine Email Adresse ein, um dein Passwort zurückzusetzen.",
"resetPassword.description": "Gib deine E-Mail-Adresse ein, um dein Passwort zurückzusetzen.",
"resetPassword.notify.success": "Wir haben dir einen Link gesendet, unter dem du dein Passwort zurücksetzen kannst.",
"resetPassword.button.back": "Zurück zur Anmeldeseite",
"resetPassword.text.resetPassword": "Passwort zurücksetzen",
@@ -67,12 +68,12 @@ export default {
"account.title": "Mein Konto",
"account.card.info.title": "Kontoinformationen",
"account.card.info.username": "Benutzername",
"account.card.info.email": "Email",
"account.card.info.email": "E-Mail",
"account.notify.info.success": "Konto erfolgreich aktualisiert",
"account.card.password.title": "Passwort",
"account.card.password.old": "Altes Passwort",
"account.card.password.new": "Neues Passwort",
"account.card.password.noPasswordSet": "Du hast kein Passwort erstellt. Wenn Du Dich mit E-Mail und Passwort anmelden möchtest, musst Du ein Passwort festlegen.",
"account.card.password.noPasswordSet": "Du hast kein Passwort erstellt. Wenn du dich mit E-Mail und Passwort anmelden möchtest, musst du ein Passwort festlegen.",
"account.notify.password.success": "Passwort erfolgreich geändert",
"account.card.oauth.title": "Anmeldung über soziale Netzwerke",
"account.card.oauth.github": "GitHub",
@@ -84,7 +85,7 @@ export default {
"account.card.oauth.unlink": "Verknüpfung aufheben",
"account.card.oauth.unlinked": "Verknüpfung aufgehoben",
"account.modal.unlink.title": "Kontoverknüpfung aufheben",
"account.modal.unlink.description": "Das Entfernen der Verknüpfung mit Deinem sozialen Konten kann dazu führen, dass Du Dein Konto verlierst, wenn Du Dich nicht an Deinen Benutzernamen und Dein Passwort erinnerst.",
"account.modal.unlink.description": "Die Aufhebung der Verknüpfung mit deinen sozialen Konten kann dazu führen, dass du dein Konto verlierst, wenn du dich nicht an deine Anmeldedaten erinnerst",
"account.notify.oauth.unlinked.success": "Verknüpfung erfolgreich aufgehoben",
"account.card.security.title": "Sicherheit",
"account.card.security.totp.enable.description": "Gib dein aktuelles Passwort ein, um TOTP zu aktivieren",
@@ -121,18 +122,18 @@ export default {
"account.shares.table.description": "Beschreibung",
"account.shares.table.visitors": "Besucher",
"account.shares.table.expiresAt": "Läuft ab am",
"account.shares.table.createdAt": "Erstellt am",
"account.shares.table.createdAt": "Angelegt am",
"account.shares.table.size": "Größe",
"account.shares.modal.share-informations": "Teile deine Information",
"account.shares.modal.share-link": "Freigabe teilen",
"account.shares.modal.delete.title": "Lösche Freigabe {share}",
"account.shares.modal.delete.description": "Möchtest du wirklich diese Freigabe löschen?",
"account.shares.modal.delete.title": "Freigabe löschen: {share}",
"account.shares.modal.delete.description": "Möchtest du diese Freigabe wirklich löschen?",
// END /account/shares
// /account/reverseShares
"account.reverseShares.title": "Externe Freigaben",
"account.reverseShares.description": "Eine externe Freigabe erlaubt dir eine einzigartige URL zu erstellen, die externen Benutzern erlaubt Dateien hochzuladen.",
"account.reverseShares.description": "Eine externe Freigabe erlaubt dir eine einzigartige URL zu erstellen, die externen Benutzern erlaubt, Dateien hochzuladen.",
"account.reverseShares.title.empty": "Es ist leer hier 👀",
"account.reverseShares.description.empty": "Du hast keine externen Freigaben erstellt.",
"account.reverseShares.description.empty": "Du hast keine externen Freigaben.",
// showCreateReverseShareModal.tsx
"account.reverseShares.modal.title": "Externe Freigabe erstellen",
"account.reverseShares.modal.expiration.label": "Gültig bis",
@@ -148,9 +149,13 @@ export default {
"account.reverseShares.modal.expiration.month-plural": "Monate",
"account.reverseShares.modal.expiration.year-singular": "Jahr",
"account.reverseShares.modal.expiration.year-plural": "Jahre",
"account.reverseShares.modal.max-size.label": "Max. Freigabengröße",
"account.reverseShares.modal.send-email": "Email Benachrichtigung senden",
"account.reverseShares.modal.send-email.description": "Sendet eine Email Benachrichtigung, wenn eine Datei auf einer externen Freigabe hochgeladen wurde.",
"account.reverseShares.modal.max-size.label": "Maximale Freigabegröße",
"account.reverseShares.modal.send-email": "E-Mail Benachrichtigungen senden",
"account.reverseShares.modal.send-email.description": "Sendet eine E-Mail Benachrichtigung, wenn eine Datei auf Ihrer externen Freigabe hochgeladen wurde.",
"account.reverseShares.modal.simplified": "Einfacher Modus",
"account.reverseShares.modal.simplified.description": "Mache es der Person, die die Datei hochlädt, einfach, sie mit dir zu teilen. Sie werden nur den Namen und die Beschreibung der Freigabe ändern können.",
"account.reverseShares.modal.public-access": "Öffentlicher Zugriff",
"account.reverseShares.modal.public-access.description": "Mache die Freigaben die mit dieser externen Freigabe erstellt werden öffentlich. Wenn dies deaktiviert ist, haben nur du und der Ersteller der Freigabe Zugriff darauf.",
"account.reverseShares.modal.max-use.label": "Maximale Nutzungen",
"account.reverseShares.modal.max-use.description": "Die maximale Anzahl von Verwendungen der URL, um Dateien hochzuladen.",
"account.reverseShare.never-expires": "Diese externe Freigabe wird nicht ablaufen.",
@@ -163,55 +168,58 @@ export default {
"account.reverseShares.table.max-size": "Maximale Freigabegröße",
"account.reverseShares.table.expires": "Läuft ab am",
"account.reverseShares.modal.reverse-share-link": "Link zu externer Freigabe",
"account.reverseShares.modal.delete.title": "Lösche externe Freigabe",
"account.reverseShares.modal.delete.description": "Möchtest du wirklich diese externe Freigabe löschen? In diesem Falle werden auch hiermit verbundene Freigaben gelöscht.",
"account.reverseShares.modal.delete.title": "Externe Freigabe löschen",
"account.reverseShares.modal.delete.description": "Möchtest du diese externe Freigabe wirklich löschen? In diesem Fall werden auch hiermit verbundene Freigaben gelöscht.",
// END /account/reverseShares
// /admin
"admin.title": "Verwaltung",
"admin.button.users": "Benutzerverwaltung",
"admin.button.shares": "Freigabenverwaltung",
"admin.button.shares": "Freigaben Verwaltung",
"admin.button.config": "Konfiguration",
"admin.version": "Version",
// END /admin
// /admin/users
"admin.users.title": "Benutzerverwaltung",
"admin.users.table.username": "Benutzername",
"admin.users.table.email": "Email",
"admin.users.table.email": "E-Mail",
"admin.users.table.admin": "Administrator",
"admin.users.edit.update.title": "Benutzer {username} aktualisieren",
"admin.users.edit.update.title": "Benutzer bearbeiten: {username}",
"admin.users.edit.update.admin-privileges": "Administratorrechte",
"admin.users.edit.update.change-password.title": "Passwort ändern",
"admin.users.edit.update.change-password.field": "Neues Passwort",
"admin.users.edit.update.change-password.button": "Neues Passwort speichern",
"admin.users.edit.update.notify.password.success": "Passwort erfolgreich geändert",
"admin.users.edit.delete.title": "Löschen des Nutzers {username}",
"admin.users.edit.delete.title": "Benutzer löschen: {username}?",
"admin.users.edit.delete.description": "Möchtest du wirklich diesen Benutzer und all seine Freigaben löschen?",
// showCreateUserModal.tsx
"admin.users.modal.create.title": "Benutzer erstellen",
"admin.users.modal.create.username": "Benutzername",
"admin.users.modal.create.email": "Email",
"admin.users.modal.create.email": "E-Mail",
"admin.users.modal.create.password": "Passwort",
"admin.users.modal.create.manual-password": "Passwort manuell festlegen",
"admin.users.modal.create.manual-password.description": "Wenn nicht aktiviert, erhält der Benutzer eine Email mit einem Link, um sein Passwort festzulegen.",
"admin.users.modal.create.manual-password.description": "Wenn nicht aktiviert, erhält der Benutzer eine E-Mail mit einem Link, um sein Passwort festzulegen.",
"admin.users.modal.create.admin": "Administratorrechte",
"admin.users.modal.create.admin.description": "Wenn aktiviert, kann der Benutzer auf das Administrator-Panel zugreifen.",
// END /admin/users
// /admin/shares
"admin.shares.title": "Freigabenverwaltung",
"admin.shares.table.id": "ID teilen",
"admin.shares.title": "Freigaben Verwaltung",
"admin.shares.table.id": "Freigabe ID",
"admin.shares.table.username": "Ersteller",
"admin.shares.table.visitors": "Besucher",
"admin.shares.table.expires": "Läuft ab am",
"admin.shares.edit.delete.title": "Lösche Freigabe {id}",
"admin.shares.edit.delete.description": "Möchtest du wirklich diese Freigabe löschen?",
"admin.shares.edit.delete.title": "Freigabe löschen: {id}",
"admin.shares.edit.delete.description": "Möchtest du diese Freigabe wirklich löschen?",
// END /admin/shares
// /upload
"upload.title": "Upload",
"upload.notify.confirm-leave": "Bist du sicher, dass du diese Seite verlassen willst? Dein Upload wird abgebrochen.",
"upload.notify.generic-error": "Während der Erstellung der Freigabe ist ein Fehler aufgetreten.",
"upload.notify.count-failed": "{count} Dateien konnten nicht hochgeladen werden. Wird erneut versucht.",
"upload.reverse-share.error.invalid.title": "Ungültiger Link zu externer Freigabe",
"upload.reverse-share.error.invalid.description": "Diese externe Freigabe ist abgelaufen oder ungültig.",
// Dropzone.tsx
"upload.dropzone.title": "Dateien hochladen",
"upload.dropzone.description": "Ziehe Dateien hierher, um deine Freigabe zu starten. Wir können nur Dateien akzeptieren, die insgesamt weniger als {maxSize} groß sind.",
"upload.dropzone.description": "Ziehe Dateien per Drag'n'Drop hierher, um deine Freigabe zu starten. Wir akzeptieren nur Dateien mit einer Gesamtgröße von bis zu {maxSize}.",
"upload.dropzone.notify.file-too-big": "Ihre Dateien überschreiten die maximale Freigabegröße von {maxSize}.",
// FileList.tsx
"upload.filelist.name": "Name",
@@ -222,9 +230,9 @@ export default {
"upload.modal.link.error.taken": "Dieser Link wird bereits verwendet",
"upload.modal.not-signed-in": "Du bist nicht angemeldet",
"upload.modal.not-signed-in-description": "Du wirst deine Freigabe nicht löschen können oder die Besucheranzahl sehen.",
"upload.modal.expires.never": "niemals",
"upload.modal.expires.never-long": "Läuft nicht ab",
"upload.modal.expires.error.too-long": "Ablauf überschreitet das maximale Ablaufdatum von {max}.",
"upload.modal.expires.never": "nie",
"upload.modal.expires.never-long": "Permanente Freigabe",
"upload.modal.expires.error.too-long": "Das Ablaufdatum überschreitet das Maximum von {max}.",
"upload.modal.link.label": "Link",
"upload.modal.expires.label": "Gültig bis",
"upload.modal.expires.minute-singular": "Minute",
@@ -238,13 +246,13 @@ export default {
"upload.modal.expires.month-singular": "Monat",
"upload.modal.expires.month-plural": "Monate",
"upload.modal.expires.year-singular": "Jahr",
"upload.modal.expires.year-plural": "Year",
"upload.modal.expires.year-plural": "Jahre",
"upload.modal.accordion.name-and-description.title": "Name und Beschreibung",
"upload.modal.accordion.name-and-description.name.placeholder": "Name",
"upload.modal.accordion.name-and-description.description.placeholder": "Hinweis für die Empfänger dieser Freigabe",
"upload.modal.accordion.email.title": "Email Empfänger",
"upload.modal.accordion.email.placeholder": "Email der Empfänger eingeben",
"upload.modal.accordion.email.invalid-email": "Ungültige Emailadresse",
"upload.modal.accordion.email.title": "E-Mail-Empfänger",
"upload.modal.accordion.email.placeholder": "E-Mail der Empfänger eingeben",
"upload.modal.accordion.email.invalid-email": "Ungültige E-Mail-Adresse",
"upload.modal.accordion.security.title": "Sicherheitseinstellungen",
"upload.modal.accordion.security.password.label": "Passwortschutz",
"upload.modal.accordion.security.password.placeholder": "Kein Passwort",
@@ -254,6 +262,7 @@ export default {
"upload.modal.completed.never-expires": "Diese Freigabe läuft niemals ab.",
"upload.modal.completed.expires-on": "Diese Freigabe wird am {expiration} ablaufen.",
"upload.modal.completed.share-ready": "Freigabe bereit",
"upload.modal.completed.notified-reverse-share-creator": "Wir haben den Ersteller der externen Freigabe benachrichtigt. Du kannst den Link auch auf andere Wege teilen.",
// END /upload
// /share/[id]
"share.title": "Freigabe {shareId}",
@@ -263,17 +272,19 @@ export default {
"share.error.removed.title": "Freigabe entfernt",
"share.error.not-found.title": "Freigabe nicht gefunden",
"share.error.not-found.description": "Die gesuchte Freigabe existiert nicht.",
"share.error.access-denied.title": "Private Freigabe",
"share.error.access-denied.description": "Das aktuelle Konto hat keine Berechtigung, um auf diese Freigabe zuzugreifen",
"share.modal.password.title": "Passwort erforderlich",
"share.modal.password.description": "Um auf diese Freigabe zuzugreifen, gib bitte das Passwort für die Freigabe ein.",
"share.modal.password.description": "Bitte gib das Passwort ein, um auf diese Freigabe zuzugreifen.",
"share.modal.password": "Passwort",
"share.modal.error.invalid-password": "Ungültiges Passwort",
"share.button.download-all": "Alles herunterladen",
"share.notify.download-all-preparing": "Die Freigabe wird vorbereitet. Versuche es in ein paar Minuten erneut.",
"share.notify.download-all-preparing": "Die Freigabe wird vorbereitet. Bitte versuche es in ein paar Minuten erneut.",
"share.modal.file-link": "Dateilink",
"share.table.name": "Name",
"share.table.size": "Größe",
"share.modal.file-preview.error.not-supported.title": "Vorschau wird nicht unterstützt",
"share.modal.file-preview.error.not-supported.description": "Eine Vorschau für diesen Dateityp wird nicht unterstützt. Bitte lade die Datei herunter, um sie anzusehen.",
"share.modal.file-preview.error.not-supported.description": "Vorschaubilder werden für diesen Dateityp nicht unterstützt. Bitte lade die Datei herunter, um sie anzuzeigen.",
// END /share/[id]
// /share/[id]/edit
"share.edit.title": "{shareId} bearbeiten",
@@ -287,11 +298,13 @@ export default {
"admin.config.category.share": "Freigabe",
"admin.config.category.email": "E-Mail",
"admin.config.category.smtp": "SMTP",
"admin.config.category.oauth": "OAuth-Anmeldung",
"admin.config.category.oauth": "Anmeldung über soziale Netzwerke",
"admin.config.general.app-name": "App-Name",
"admin.config.general.app-name.description": "Name der Applikation",
"admin.config.general.app-url": "App-URL",
"admin.config.general.app-url.description": "Auf welcher URL Pingvin Share verfügbar ist",
"admin.config.general.secure-cookies": "Sichere Cookies",
"admin.config.general.secure-cookies.description": "Gibt an, ob die secure flag bei Cookies gesetzt werden soll. Falls aktiviert, wird die Seite nicht funktionieren, wenn sie über HTTP aufgerufen wird.",
"admin.config.general.show-home-page": "Startseite anzeigen",
"admin.config.general.show-home-page.description": "Ob die Startseite angezeigt werden soll",
"admin.config.general.session-duration": "Session-Dauer",
@@ -299,40 +312,42 @@ export default {
"admin.config.general.logo": "Logo",
"admin.config.general.logo.description": "Ändere dein Logo durch Hochladen eines Bildes. Das Bild muss im PNG-Format vorliegen und sollte mit Seitenverhältnis 1:1 sein.",
"admin.config.general.logo.placeholder": "Bild auswählen",
"admin.config.email.enable-share-email-recipients": "Erlaube das Teilen der Freigabe via Email",
"admin.config.email.enable-share-email-recipients.description": "Gibt an, ob Emails an Freigabe-Empfänger ermöglicht werden sollen. Aktiviere dies nur, wenn Du SMTP aktivierst hast.",
"admin.config.email.enable-share-email-recipients": "Erlaube das Teilen der Freigabe via E-Mail",
"admin.config.email.enable-share-email-recipients.description": "Gibt an, ob das Teilen von E-Mails mit Empfängern erlaubt werden soll. Aktiviere dies nur, wenn SMTP aktiviert ist.",
"admin.config.email.share-recipients-subject": "Betreff für Freigabe-Empfänger",
"admin.config.email.share-recipients-subject.description": "Betreff der E-Mail, die an die Freigabe-Empfänger gesendet wird.",
"admin.config.email.share-recipients-message": "Nachricht für Freigabe-Empfänger",
"admin.config.email.share-recipients-message.description": "Nachricht, die an die Freigabe-Empfänger gesendet wird. Verfügbare Variablen:\n- {creator} - Der Benutzername des Erstellers der Freigabe\n- {shareUrl} - Die URL der Freigabe\n- {desc} - Die Beschreibung der Freigabe\n- {expires} - Das Ablaufdatum der Freigabe\nVariablen werden durch die tatsächlichen Werte ersetzt.",
"admin.config.email.share-recipients-message.description": "Nachricht, die an die Freigabe-Empfänger gesendet wird. Verfügbare Variablen:\n- {creator} - Der Benutzername des Erstellers der Freigabe\n- {creatorEmail} - E-Mail-Adresse des Erstellers der Freigabe\n- {shareUrl} - Die URL der Freigabe\n- {desc} - Die Beschreibung der Freigabe\n- {expires} - Das Ablaufdatum der Freigabe\nVariablen werden durch die tatsächlichen Werte ersetzt.",
"admin.config.email.reverse-share-subject": "Name der externen Freigabe",
"admin.config.email.reverse-share-subject.description": "Betreff der Email, die gesendet wird, wenn jemand eine Datei mit deinem externen Freigabe-Link hochlädt.",
"admin.config.email.reverse-share-subject.description": "Betreff der gesendeten E-Mail, wenn jemand eine Freigabe mit Ihrem externen Freigabe-Link erstellt hat.",
"admin.config.email.reverse-share-message": "Nachricht für externe Freigabe",
"admin.config.email.reverse-share-message.description": "Nachricht, die gesendet wird, wenn jemand eine Freigabe mit deinem externen Freigabe-Link erstellt. {shareUrl} wird durch den Namen des Erstellers und die Freigabe-URL ersetzt.",
"admin.config.email.reset-password-subject": "Betreff für Passwortzurücksetzung",
"admin.config.email.reset-password-subject.description": "Betreff der E-Mail, die gesendet wird, wenn ein Benutzer eine Passwortzurücksetzung anfordert.",
"admin.config.email.reset-password-subject.description": "Betreff der gesendeten E-Mail, wenn ein Benutzer ein Passwort zurücksetzen möchte.",
"admin.config.email.reset-password-message": "Nachricht für Passwortzurücksetzung",
"admin.config.email.reset-password-message.description": "Nachricht, die gesendet wird, wenn ein Benutzer eine Passwortzurücksetzung anfordert. {url} wird durch die URL für das Zurücksetzen des Passworts ersetzt.",
"admin.config.email.invite-subject": "Betreff für Einladung",
"admin.config.email.invite-subject.description": "Betreff der E-Mail, die gesendet wird, wenn ein Administrator einen Benutzer einlädt.",
"admin.config.email.invite-subject.description": "Betreff der gesendeten E-Mail, wenn ein Administrator einen Benutzer einlädt.",
"admin.config.email.invite-message": "Nachricht für Einladung",
"admin.config.email.invite-message.description": "Nachricht, die gesendet wird, wenn ein Administrator einen Benutzer einlädt. {url} wird durch die Einladungs-URL und {password} durch das Passwort ersetzt.",
"admin.config.email.invite-message.description": "Nachricht, die gesendet wird, wenn ein Administrator einen Benutzer einlädt. {url} wird durch die Einladung-URL ersetzt und {password} durch das Passwort ersetzt.",
"admin.config.share.allow-registration": "Registrierung erlauben",
"admin.config.share.allow-registration.description": "Gibt an, ob eine Registrierung erlaubt ist",
"admin.config.share.allow-unauthenticated-shares": "Nicht authentifizierte Freigaben erlauben",
"admin.config.share.allow-unauthenticated-shares.description": "Gibt an, ob nicht authentifizierte Benutzer Freigaben erstellen können",
"admin.config.share.max-expiration": "Max. Ablaufdatum",
"admin.config.share.max-expiration.description": "Maximale Ablaufzeit in Stunden. Auf 0 setzen, um kein Ablaufdatum zu definieren.",
"admin.config.share.share-id-length": "Default share ID length",
"admin.config.share.share-id-length.description": "Default length for the generated ID of a share. This value is also used to generate links for reverse shares. A value below 8 is not considered secure.",
"admin.config.share.max-size": "Maximale Größe",
"admin.config.share.max-size.description": "Maximale Größe einer Freigabe in Bytes",
"admin.config.share.zip-compression-level": "Zip Komprimierungsstufe",
"admin.config.share.zip-compression-level.description": "Passe den Wert an, um ein Gleichgewicht zwischen Dateigröße und Komprimierungsgeschwindigkeit herzustellen. Gültige Werte liegen zwischen 0 und 9, wobei 0 für keine Komprimierung und 9 für maximale Komprimierung steht.",
"admin.config.share.chunk-size": "Chunkgröße",
"admin.config.share.chunk-size.description": "Passe die Chunkgröße (in Bytes) für deine Uploads an, um die Zuverlässigkeit deiner Internetverbindung auszugleichen. Kleinere Chunks können die Erfolgsraten für instabile Verbindungen verbessern, während größere Chunks Uploads für stabile Verbindungen beschleunigen können.",
"admin.config.share.auto-open-share-modal": "Auto open create share modal",
"admin.config.share.auto-open-share-modal.description": "The share creation modal automatically appears when a user selects files, eliminating the need to manually click the button.",
"admin.config.smtp.enabled": "Aktiviert",
"admin.config.smtp.enabled.description": "Gibt an, ob SMTP aktiviert ist. Aktiviere dies nur, wenn Du den Host, den Port, die Email, den Benutzernamen und das Passwort deines SMTP-Servers eingegeben hast.",
"admin.config.share.zip-compression-level": "ZIP-Kompressionslevel",
"admin.config.share.zip-compression-level.description": "Passe den Wert an, um ein Gleichgewicht zwischen Dateigröße und Kompressionsgeschwindigkeit herzustellen. Gültige Werte liegen zwischen 0 und 9, wobei 0 für keine Komprimierung und 9 für maximale Komprimierung steht. ",
"admin.config.share.chunk-size": "Chunk Größe",
"admin.config.share.chunk-size.description": "Passe die Chunk-Größe (in Bytes) für deine Uploads an, um Effizienz und Zuverlässigkeit entsprechend deiner Internetverbindung auszubalancieren. Kleinere Chunks können die Erfolgsraten bei instabilen Verbindungen erhöhen, während größere Chunks Uploads bei stabilen Verbindungen beschleunigen.",
"admin.config.share.auto-open-share-modal": "Freigabe-Fenster automatisch öffnen",
"admin.config.share.auto-open-share-modal.description": "Das Freigabe-Fenster erscheint automatisch, sobald ein Benutzer Dateien ausgewählt hat, ohne extra auf den Button klicken zu müssen.",
"admin.config.smtp.enabled": "Aktivieren",
"admin.config.smtp.enabled.description": "Gibt an, ob SMTP aktiviert ist. Aktiviere dies nur, wenn du den Host, den Port, die E-Mail, den Benutzernamen und das Passwort deines SMTP-Servers eingegeben hast.",
"admin.config.smtp.host": "Host",
"admin.config.smtp.host.description": "Host des SMTP-Servers",
"admin.config.smtp.port": "Port",
@@ -344,48 +359,83 @@ export default {
"admin.config.smtp.password": "Passwort",
"admin.config.smtp.password.description": "Passwort des SMTP-Servers",
"admin.config.smtp.button.test": "Test-E-Mail senden",
"admin.config.smtp.allow-unauthorized-certificates": "Vertrauen von nicht authentifizierten SMTP-Server-Zertifikaten",
"admin.config.smtp.allow-unauthorized-certificates.description": "Verwende diese Option nur, wenn du selbst signierten Zertifikaten vertrauen musst.",
"admin.config.oauth.allow-registration": "Registrierung erlauben",
"admin.config.oauth.allow-registration.description": "Benutzern erlauben, sich über Soziale Netzwerke zu registrieren",
"admin.config.oauth.allow-registration.description": "Benutzern erlauben, sich über soziale Netzwerke zu registrieren",
"admin.config.oauth.ignore-totp": "TOTP ignorieren",
"admin.config.oauth.ignore-totp.description": "Gibt an, ob TOTP ignoriert werden soll, wenn sich der Benutzer über Soziale Netzwerke anmeldet",
"admin.config.oauth.ignore-totp.description": "Gibt an, ob TOTP ignoriert werden soll, wenn sich der Benutzer über soziale Netzwerke anmeldet",
"admin.config.oauth.disable-password": "Anmelden mit Passwort deaktivieren",
"admin.config.oauth.disable-password.description": "Deaktiviert das Anmelden mit Passwort\nStelle vor Aktivierung dieser Konfiguration sicher, dass ein OAuth-Provider korrekt konfiguriert ist, um nicht ausgesperrt zu werden.",
"admin.config.oauth.github-enabled": "GitHub",
"admin.config.oauth.github-enabled.description": "GitHub Anmeldung erlaubt",
"admin.config.oauth.github-client-id": "GitHub Client-ID",
"admin.config.oauth.github-client-id.description": "Client-ID der GitHub OAuth-App",
"admin.config.oauth.github-client-secret": "GitHub Client-Secret",
"admin.config.oauth.github-client-secret.description": "Client-Secret der GitHub OAuth-App",
"admin.config.oauth.github-client-secret": "GitHub Client Secret",
"admin.config.oauth.github-client-secret.description": "Client Secret der GitHub OAuth App",
"admin.config.oauth.google-enabled": "Google",
"admin.config.oauth.google-enabled.description": "Google Anmeldung erlaubt",
"admin.config.oauth.google-client-id": "Google Client-ID",
"admin.config.oauth.google-client-id.description": "Client-ID der Google OAuth-App",
"admin.config.oauth.google-client-secret": "Google Client-Secret",
"admin.config.oauth.google-client-secret.description": "Client-Secret der Google OAuth-App",
"admin.config.oauth.google-client-secret": "Google Client Secret",
"admin.config.oauth.google-client-secret.description": "Client Secret der Google OAuth App",
"admin.config.oauth.microsoft-enabled": "Microsoft",
"admin.config.oauth.microsoft-enabled.description": "Microsoft Anmeldung erlaubt",
"admin.config.oauth.microsoft-tenant": "Microsoft Mandant",
"admin.config.oauth.microsoft-tenant.description": "Mandant-ID der Microsoft OAuth App\ncommon: Benutzer mit einem persönlichen Microsoft-Konto und einem Arbeits- oder Schulkonto von Microsoft Entra ID können sich in der Anwendung anmelden.\norganizations: Nur Benutzer mit Arbeits- oder Schulkonten von Microsoft Entra ID können sich in der Anwendung anmelden.\nconsumers: Nur Benutzer mit einem persönlichen Microsoft-Konto können sich in der Anwendung anmelden.\nDomänenname des Microsoft Entra Mandanten oder die Mandanten-ID im GUID-Format: Nur Benutzer eines bestimmten Microsoft Entra Mandanten (Verzeichnismitglieder mit einem Arbeits- oder Schulkonto oder Verzeichnis Gäste mit einem persönlichen Microsoft-Konto) können sich anmelden.",
"admin.config.oauth.microsoft-client-id": "Microsoft Client-ID",
"admin.config.oauth.microsoft-client-id.description": "Client-ID der Microsoft OAuth-App",
"admin.config.oauth.microsoft-client-secret": "Microsoft Client-Secret",
"admin.config.oauth.microsoft-client-secret.description": "Client-Secret der Microsoft OAuth-App",
"admin.config.oauth.microsoft-client-secret": "Microsoft Client Secret",
"admin.config.oauth.microsoft-client-secret.description": "Client Secret der Microsoft OAuth App",
"admin.config.oauth.discord-enabled": "Discord",
"admin.config.oauth.discord-enabled.description": "Discord Anmeldung erlaubt",
"admin.config.oauth.discord-limited-users": "Discord limitierte Benutzer",
"admin.config.oauth.discord-limited-users.description": "Limitiere die Anmeldung für spezifische Benutzer anhand ihrer Discord ID. Lasse dieses Feld leer, um es zu deaktivieren.",
"admin.config.oauth.discord-limited-guild": "Discord Server-ID",
"admin.config.oauth.discord-limited-guild.description": "Die Anmeldung auf Benutzer in einem bestimmten Server beschränken. Leer lassen, um zu deaktivieren.",
"admin.config.oauth.discord-client-id": "Discord Client-ID",
"admin.config.oauth.discord-client-id.description": "Client-ID der Discord OAuth-App",
"admin.config.oauth.discord-client-secret": "Discord Client-Secret",
"admin.config.oauth.discord-client-secret.description": "Client-Secret der Discord OAuth-App",
"admin.config.oauth.discord-client-secret": "Discord Client Secret",
"admin.config.oauth.discord-client-secret.description": "Client Secret der Discord OAuth App",
"admin.config.oauth.oidc-enabled": "OpenID Connect",
"admin.config.oauth.oidc-enabled.description": "OpenID Connect Anmeldung erlaubt",
"admin.config.oauth.oidc-discovery-uri": "OpenID Verbindung Discovery URL",
"admin.config.oauth.oidc-discovery-uri.description": "Discovery-URL der OpenID OAuth App",
"admin.config.oauth.oidc-sign-out": "Abmelden von OpenID Connect",
"admin.config.oauth.oidc-sign-out.description": "Wenn aktiviert, wird der Benutzer mit der „Abmelden“-Schaltfläche vom OpenID-Connect-Provider abgemeldet.",
"admin.config.oauth.oidc-scope": "OpenID Connect scope",
"admin.config.oauth.oidc-scope.description": "Scopes which should be requested from the OpenID Connect provider.",
"admin.config.oauth.oidc-username-claim": "OpenID Connect Benutzername anfordern",
"admin.config.oauth.oidc-username-claim.description": "Benutzername im OpenID Token. Leer lassen, wenn du nicht weißt, was diese Konfiguration bedeutet.",
"admin.config.oauth.oidc-role-path": "Pfad zu den Rollen im OpenID Verbindungs-Token",
"admin.config.oauth.oidc-role-path.description": "Muss ein valider JMES-Pfad sein, der zu einem Array an Rollen führt. " + "Die Zugangsverwaltung über Rollen in OpenID Connect ist nur empfohlen, wenn kein anderer Identitätsprovider konfiguriert und die Anmeldung per Password deaktiviert ist. " + "Leer lassen, wenn du nicht weißt, was diese Konfiguration bedeutet.",
"admin.config.oauth.oidc-role-general-access": "OpenID Connect Rolle für allgemeinen Zugriff",
"admin.config.oauth.oidc-role-general-access.description": "Rolle für generellen Zugriff. Muss Teil der Rollen eines Benutzers sein, damit dieser sich anmelden kann. " + "Leer lassen, wenn du nicht weißt, was diese Konfiguration bedeutet.",
"admin.config.oauth.oidc-role-admin-access": "OpenID Connect Rolle für Admin-Zugriff",
"admin.config.oauth.oidc-role-admin-access.description": "Rolle für administrativen Zugriff. Muss Teil der Rollen eines Benutzers sein, damit dieser auf das Administrator-Panel zugreifen kann. " + "Leer lassen, wenn du nicht weißt, was diese Konfiguration bedeutet.",
"admin.config.oauth.oidc-client-id": "OpenID Connect Client-ID",
"admin.config.oauth.oidc-client-id.description": "Client-ID der OpenID Connect OAuth-App",
"admin.config.oauth.oidc-client-secret": "OpenID Connect Client-Secret",
"admin.config.oauth.oidc-client-secret.description": "Client-Secret der OpenID Connect OAuth-App",
"admin.config.category.ldap": "LDAP",
"admin.config.ldap.enabled": "LDAP aktivieren",
"admin.config.ldap.enabled.description": "LDAP-Authentifizierung für die Benutzeranmeldung verwenden",
"admin.config.ldap.url": "Server-URL",
"admin.config.ldap.url.description": "URL des LDAP-Servers",
"admin.config.ldap.bind-dn": "Binde DN",
"admin.config.ldap.bind-dn.description": "Standard-Benutzer, der zur Benutzersuche verwendet wird",
"admin.config.ldap.bind-password": "Binde Passwort",
"admin.config.ldap.bind-password.description": "Passwort der zur Benutzersuche verwendet wird",
"admin.config.ldap.search-base": "Benutzerbasis",
"admin.config.ldap.search-base.description": "Basisstandort, an dem die Benutzersuche durchgeführt wird",
"admin.config.ldap.search-query": "Benutzerabfrage",
"admin.config.ldap.search-query.description": "Die Benutzer Abfrage wird in der \"Benutzerdatenbank\" gesucht für den LDAP Benutzer. %username% kann als Platzhalter für den Benutzer eingegeben werden.",
"admin.config.ldap.admin-groups": "Admin-Gruppe",
"admin.config.ldap.admin-groups.description": "Gruppe benötigt für den Administrationszugang.",
"admin.config.ldap.field-name-member-of": "Benutzergruppen-Attributname",
"admin.config.ldap.field-name-member-of.description": "LDAP-Attributname für die Gruppen, in denen ein Benutzer Mitglied ist. Dies wird bei der Überprüfung der Admin-Gruppe verwendet.",
"admin.config.ldap.field-name-email": "Attributname für die E-Mail-Adresse des Benutzers",
"admin.config.ldap.field-name-email.description": "LDAP-Attributname für die E-Mail-Adresse eines Benutzers.",
// 404
"404.description": "Ups, diese Seite existiert nicht.",
"404.button.home": "Zurück zur Startseite",
@@ -393,7 +443,7 @@ export default {
"error.title": "Fehler",
"error.description": "Ups!",
"error.button.back": "Zurück",
"error.msg.default": "Etwas ist schief gelaufen.",
"error.msg.default": "Etwas ist schiefgelaufen.",
"error.msg.access_denied": "Du hast den Authentifizierungsprozess abgebrochen, bitte versuche es erneut.",
"error.msg.expired_token": "Der Authentifizierungsprozess hat zu lange gedauert, bitte versuche es erneut.",
"error.msg.invalid_token": "Interner Fehler",
@@ -401,9 +451,9 @@ export default {
"error.msg.no_email": "Kann die E-Mail-Adresse von dem Konto {0} nicht abrufen.",
"error.msg.already_linked": "Das Konto {0} ist bereits mit einem anderen Konto verknüpft.",
"error.msg.not_linked": "Das Konto {0} wurde noch nicht mit einem Konto verknüpft.",
"error.msg.unverified_account": "Dieses Konto {0} wurde noch nicht verifiziert, bitte versuchen Sie es nach der Verifikation erneut.",
"error.msg.discord_guild_permission_denied": "Du bist nicht berechtigt, Dich anzumelden.",
"error.msg.cannot_get_user_info": "Deine Benutzerinformationen können nicht von diesem Konto {0} abgerufen werden.",
"error.msg.unverified_account": "Dieses Konto {0} wurde noch nicht verifiziert, bitte versuche es nach der Verifikation erneut.",
"error.msg.user_not_allowed": "Du bist nicht berechtigt, dich anzumelden.",
"error.msg.cannot_get_user_info": "Ihre Benutzerinformationen können von diesem {0} Konto nicht abgerufen werden.",
"error.param.provider_github": "GitHub",
"error.param.provider_google": "Google",
"error.param.provider_microsoft": "Microsoft",
@@ -423,6 +473,7 @@ export default {
"common.text.link": "Link",
"common.text.navigate-to-link": "Link öffnen",
"common.text.or": "oder",
"common.text.redirecting": "Umleitung...",
"common.button.go-back": "Zurück",
"common.button.go-home": "Zur Startseite",
"common.notify.copied": "Dein Link wurde in die Zwischenablage kopiert",
@@ -432,6 +483,8 @@ export default {
"common.error.invalid-email": "Ungültige E-Mail-Adresse",
"common.error.too-short": "Muss mindestens {length} Zeichen enthalten",
"common.error.too-long": "Muss maximal {length} Zeichen enthalten",
"common.error.number-too-small": "Darf mindestens {min} sein",
"common.error.number-too-large": "Darf höchstens {max} sein",
"common.error.exact-length": "Muss genau {length} Zeichen lang sein",
"common.error.invalid-number": "Muss eine Zahl sein",
"common.error.field-required": "Dieses Feld ist erforderlich"

View File

@@ -3,7 +3,7 @@ export default {
"navbar.upload": "Μεταφόρτωση",
"navbar.signin": "Είσοδος",
"navbar.home": "Αρχική",
"navbar.signup": "Εγγραφή",
"navbar.signup": "Sign up",
"navbar.links.shares": "",
"navbar.links.reverse": "Αντίστροφος σύνδεσμος κοινής χρήσης",
"navbar.avatar.account": "Ο λογαριασμός μου",
@@ -16,9 +16,9 @@ export default {
"home.bullet.a.name": "Ιδιωτική εγκατάσταση",
"home.bullet.a.description": "Φιλοξενήστε το Pingvin Share στο δικό σας μηχάνημα.",
"home.bullet.b.name": "Απόρρητο",
"home.bullet.b.description": "Τα αρχεία σας είναι αρχεία σας και δεν πρέπει ποτέ να μπείτε στα χέρια τρίτων.",
"home.bullet.b.description": "Your files are yours and will never be accessed by third parties.",
"home.bullet.c.name": "Χωρίς όριο μεγέθους αρχείου",
"home.bullet.c.description": "Ανεβάστε όσο μεγάλα αρχεία θέλετε. Μόνο ο σκληρός σας δίσκος θα είναι το όριό σας.",
"home.bullet.c.description": "Upload files as big as you want. Only your hard drive will be your limit.",
"home.button.start": "Ας αρχίσουμε",
"home.button.source": "Πηγαίος κώδικας",
// END /
@@ -34,6 +34,7 @@ export default {
"signIn.notify.totp-required.title": "Απαιτείται έλεγχος ταυτότητας δύο παραγόντων.",
"signIn.notify.totp-required.description": "Παρακαλώ εισάγετε τον κωδικό 2FA.",
"signIn.oauth.or": "Ή",
"signIn.oauth.signInWith": "Σύνδεση με",
"signIn.oauth.github": "GitHub",
"signIn.oauth.google": "Google",
"signIn.oauth.microsoft": "Microsoft",
@@ -57,12 +58,12 @@ export default {
// /auth/reset-password
"resetPassword.title": "Ξεχάσατε τον κωδικό σας;",
"resetPassword.description": "A message with a link to reset your password has been sent if the email exists.",
"resetPassword.notify.success": "Εισάγετε το email σας για επαναφορά κωδικού.",
"resetPassword.notify.success": "A message with a link to reset your password has been sent if the provided email exists.",
"resetPassword.button.back": "Πίσω στη σελίδα εισόδου",
"resetPassword.text.resetPassword": "Επαναφορά κωδικού πρόσβασης",
"resetPassword.text.enterNewPassword": "Εισάγετε το νέο σας κωδικό",
"resetPassword.input.password": "Νέος κωδικός",
"resetPassword.notify.passwordReset": "Η επαναφορά του κωδικού πρόσβασης σας ολοκληρώθηκε με επιτυχία!",
"resetPassword.notify.passwordReset": "Your password has been successfully reset.",
// /account
"account.title": "Ο λογαριασμός μου",
"account.card.info.title": "Πληροφορίες λογαριασμού",
@@ -72,7 +73,7 @@ export default {
"account.card.password.title": "Κωδικόs πρόσβασης",
"account.card.password.old": "Παλιός κωδικός",
"account.card.password.new": "Νέος κωδικός",
"account.card.password.noPasswordSet": "Δεν έχετε ορίσει κωδικό πρόσβασης. Αν θέλετε να συνδεθείτε με email και κωδικό πρόσβασης, πρέπει να ορίσετε έναν κωδικό πρόσβασης.",
"account.card.password.noPasswordSet": "You do not have a password set. To sign in using your email and password, you need to create a password.",
"account.notify.password.success": "Ο κωδικός πρόσβασης άλλαξε επιτυχώς",
"account.card.oauth.title": "Σύνδεση με λογαριασμό μέσων κοινωνικού δικτύου",
"account.card.oauth.github": "GitHub",
@@ -84,7 +85,7 @@ export default {
"account.card.oauth.unlink": "Αποσύνδεση",
"account.card.oauth.unlinked": "Αποσυνδεδεμένο",
"account.modal.unlink.title": "Αποσύνδεση Λογαριασμού",
"account.modal.unlink.description": "Η αποσύνδεση των κοινωνικών λογαριασμών σας μπορεί να προκαλέσει απώλεια του λογαριασμού σας αν δε θυμάστε το όνομα χρήστη και τον κωδικό πρόσβασης.",
"account.modal.unlink.description": "Unlinking your social accounts may cause you to lose your account if you don't remember your login credentials",
"account.notify.oauth.unlinked.success": "Επιτυχής αποσύνδεση",
"account.card.security.title": "Ασφάλεια",
"account.card.security.totp.enable.description": "Εισάγετε τον τρέχοντα κωδικό σας για να ξεκινήσετε την ενεργοποίηση του TOTP",
@@ -120,12 +121,12 @@ export default {
"account.shares.table.name": "Όνομα",
"account.shares.table.description": "Περιγραφή",
"account.shares.table.visitors": "Επισκέπτες",
"account.shares.table.expiresAt": "Λήξη",
"account.shares.table.createdAt": "Δημιουργήθηκε",
"account.shares.table.expiresAt": "Expires on",
"account.shares.table.createdAt": "Created on",
"account.shares.table.size": "Μέγεθος",
"account.shares.modal.share-informations": "Πληροφορίες διαμοιρασμού",
"account.shares.modal.share-link": "Κοινοποίηση συνδέσμου",
"account.shares.modal.delete.title": "Διαγραφή κοινοποίησης {share}",
"account.shares.modal.delete.title": "Delete share: {share}",
"account.shares.modal.delete.description": "Θέλετε πραγματικά να διαγράψετε αυτό το διαμοιρασμό;",
// END /account/shares
// /account/reverseShares
@@ -149,8 +150,12 @@ export default {
"account.reverseShares.modal.expiration.year-singular": "Έτος",
"account.reverseShares.modal.expiration.year-plural": "Έτη",
"account.reverseShares.modal.max-size.label": "Μέγιστο μέγεθος κοινοποίησης",
"account.reverseShares.modal.send-email": "Αποστολή ειδοποιήσεων με email",
"account.reverseShares.modal.send-email.description": "Στείλτε μια ειδοποίηση μέσω ηλεκτρονικού ταχυδρομείου όταν δημιουργείται ένας διαμοιρασμός με αυτόν τον σύνδεσμο ανάστροφης κοινοποίησης.",
"account.reverseShares.modal.send-email": "Send email notifications",
"account.reverseShares.modal.send-email.description": "Sends you an email notification when a share is created with this reverse share link.",
"account.reverseShares.modal.simplified": "Απλή λειτουργία",
"account.reverseShares.modal.simplified.description": "Make it easy for the person uploading the file to share it with you. They will only be able to customize the name and description of the share.",
"account.reverseShares.modal.public-access": "Δημόσια πρόσβαση",
"account.reverseShares.modal.public-access.description": "Make the shares created with this reverse share public. If disabled, only you and the share creator will have access to view it.",
"account.reverseShares.modal.max-use.label": "Μέγιστες χρήσεις",
"account.reverseShares.modal.max-use.description": "Ο μέγιστος αριθμός που μπορεί να χρησιμοποιηθεί αυτό το URL για τη δημιουργία ενός διαμοιρασμού.",
"account.reverseShare.never-expires": "Αυτός ο αντίστροφος διαμοιρασμός δε λήγει.",
@@ -169,7 +174,7 @@ export default {
// /admin
"admin.title": "Διαχείριση",
"admin.button.users": "Διαχείριση χρηστών",
"admin.button.shares": "Share management",
"admin.button.shares": "Διαχείριση κοινοποιήσεων",
"admin.button.config": "Διαμόρφωση",
"admin.version": "Έκδοση",
// END /admin
@@ -178,14 +183,14 @@ export default {
"admin.users.table.username": "Όνομα χρήστη",
"admin.users.table.email": "E-mail",
"admin.users.table.admin": "Διαχειριστής",
"admin.users.edit.update.title": "Ενημέρωση χρήστη {username}",
"admin.users.edit.update.title": "Edit user: {username}",
"admin.users.edit.update.admin-privileges": "Δικαιώματα διαχειριστή",
"admin.users.edit.update.change-password.title": "Αλλαγή κωδικού πρόσβασής",
"admin.users.edit.update.change-password.field": "Νέος κωδικός πρόσβασης",
"admin.users.edit.update.change-password.button": "Αποθήκευση νέου κωδικού πρόσβασης",
"admin.users.edit.update.notify.password.success": "Ο κωδικός πρόσβασης άλλαξε επιτυχώς",
"admin.users.edit.delete.title": "Διαγραφή χρήστη {username}",
"admin.users.edit.delete.description": "Θέλετε να διαγράψετε το χρήστη και όλες τις κοινοποιήσεις του;",
"admin.users.edit.delete.title": "Delete user: {username} ?",
"admin.users.edit.delete.description": "Do you really want to delete this user and all their shares?",
// showCreateUserModal.tsx
"admin.users.modal.create.title": "Δημιουργία χρήστη",
"admin.users.modal.create.username": "Όνομα χρήστη",
@@ -197,21 +202,24 @@ export default {
"admin.users.modal.create.admin.description": "Αν ενεργοποιηθεί, ο χρήστης θα μπορεί να έχει πρόσβαση στον πίνακα διαχείρισης.",
// END /admin/users
// /admin/shares
"admin.shares.title": "Share management",
"admin.shares.table.id": "Share ID",
"admin.shares.table.username": "Creator",
"admin.shares.table.visitors": "Visitors",
"admin.shares.table.expires": "Expires At",
"admin.shares.edit.delete.title": "Delete share {id}",
"admin.shares.edit.delete.description": "Do you really want to delete this share?",
"admin.shares.title": "Διαχείριση κοινοποιήσεων",
"admin.shares.table.id": "Αναγνωριστικό κοινοποίησης",
"admin.shares.table.username": "Δημιουργός",
"admin.shares.table.visitors": "Επισκέπτες",
"admin.shares.table.expires": "Expires on",
"admin.shares.edit.delete.title": "Delete share: {id}",
"admin.shares.edit.delete.description": "Θέλετε πραγματικά να διαγράψετε αυτή τη κοινοποίηση;",
// END /admin/shares
// /upload
"upload.title": "Μεταφόρτωση",
"upload.notify.confirm-leave": "Are you sure you want to leave this page? Your upload will be canceled.",
"upload.notify.generic-error": "Παρουσιάστηκε σφάλμα κατά την ολοκλήρωση της κοινής χρήσης σας.",
"upload.notify.count-failed": "Τα αρχεία {count} απέτυχαν να μεταφορτώσουν. Δοκιμάστε ξανά.",
"upload.reverse-share.error.invalid.title": "Invalid reverse share link",
"upload.reverse-share.error.invalid.description": "This reverse share has expired or is invalid.",
// Dropzone.tsx
"upload.dropzone.title": "Μεταφόρτωση αρχείων",
"upload.dropzone.description": "Σύρετε και αποθέστε αρχεία εδώ για να ξεκινήσει η κοινή χρήση σας. Μπορούμε να δεχτούμε μόνο αρχεία που είναι λιγότερο από {maxSize} συνολικά.",
"upload.dropzone.description": "Drag'n'drop files here to start your share. We only accept files up to {maxSize} in total.",
"upload.dropzone.notify.file-too-big": "Τα αρχεία σας υπερβαίνουν το μέγιστο μέγεθος κοινής χρήσης του {maxSize}.",
// FileList.tsx
"upload.filelist.name": "Όνομα",
@@ -223,8 +231,8 @@ export default {
"upload.modal.not-signed-in": "Δεν είστε συνδεδεμένος/η",
"upload.modal.not-signed-in-description": "Δεν θα μπορείτε να διαγράψετε την κοινή χρήση σας χειροκίνητα και να δείτε την αρίθμηση επισκεπτών.",
"upload.modal.expires.never": "ποτέ",
"upload.modal.expires.never-long": "Χωρίς λήξη",
"upload.modal.expires.error.too-long": "Η λήξη υπερβαίνει τη μέγιστη ημερομηνία λήξης {max}.",
"upload.modal.expires.never-long": "Permanent share",
"upload.modal.expires.error.too-long": "Expiration date exceeds the maximum of {max}.",
"upload.modal.link.label": "Σύνδεσμος",
"upload.modal.expires.label": "Λήξη",
"upload.modal.expires.minute-singular": "Λεπτό",
@@ -239,9 +247,9 @@ export default {
"upload.modal.expires.month-plural": "Μήνες",
"upload.modal.expires.year-singular": "Έτος",
"upload.modal.expires.year-plural": "Έτη",
"upload.modal.accordion.name-and-description.title": "Name and description",
"upload.modal.accordion.name-and-description.name.placeholder": "Name",
"upload.modal.accordion.name-and-description.description.placeholder": "Note for the recipients of this share",
"upload.modal.accordion.name-and-description.title": "Όνομα και περιγραφή",
"upload.modal.accordion.name-and-description.name.placeholder": "Όνομα",
"upload.modal.accordion.name-and-description.description.placeholder": "Σημείωση για τους παραλήπτες αυτής της κοινοποίησης",
"upload.modal.accordion.email.title": "Αποδέκτες email",
"upload.modal.accordion.email.placeholder": "Εισάγετε αποδέκτες email",
"upload.modal.accordion.email.invalid-email": "Μη έγκυρη διεύθυνση e-mail",
@@ -254,6 +262,7 @@ export default {
"upload.modal.completed.never-expires": "Αυτός ο διαμοιρασμός δεν λήγει.",
"upload.modal.completed.expires-on": "Αυτός ο διαμοιρασμός θα λήξει {expiration}.",
"upload.modal.completed.share-ready": "Κοινοποίηση έτοιμου",
"upload.modal.completed.notified-reverse-share-creator": "Έχουμε ειδοποιήσει τον δημιουργό της αντίστροφης κοινής χρήσης. Μπορείτε επίσης να μοιραστείτε χειροκίνητα αυτόν τον σύνδεσμο μαζί τους μέσω άλλων μέσων.",
// END /upload
// /share/[id]
"share.title": "Διαμοιρασμός {shareId}",
@@ -263,17 +272,19 @@ export default {
"share.error.removed.title": "Κοινοποίηση αφαιρέθηκε",
"share.error.not-found.title": "Η κοινοποίηση δε βρέθηκε",
"share.error.not-found.description": "Η κοινοποίηση που ψάχνετε δεν υπάρχει.",
"share.error.access-denied.title": "Ιδιωτική κοινοποίηση",
"share.error.access-denied.description": "Ο τρέχων λογαριασμός δεν έχει δικαίωμα πρόσβασης σε αυτήν την κοινοποίηση",
"share.modal.password.title": "Απαιτείται κωδικός",
"share.modal.password.description": "Για να αποκτήσετε πρόσβαση σε αυτή την κοινοποίηση εισάγετε τον κωδικό πρόσβασης.",
"share.modal.password.description": "Please enter the password to acces this share.",
"share.modal.password": "Κωδικός πρόσβασης",
"share.modal.error.invalid-password": "Μη έγκυρος κωδικός πρόσβασης",
"share.button.download-all": "Λήψη όλων",
"share.notify.download-all-preparing": "Αυτός ο διαμοιρασμός βρίσκεται σε επεξεργασία. Προσπαθήστε ξανά σε λίγο.",
"share.notify.download-all-preparing": "The share is being prepared. Please try again in a few minutes.",
"share.modal.file-link": "Σύνδεσμος αρχείου",
"share.table.name": "Όνομα",
"share.table.size": "Μέγεθος",
"share.modal.file-preview.error.not-supported.title": "Η προεπισκόπηση δεν υποστηρίζεται",
"share.modal.file-preview.error.not-supported.description": "Η προεπισκόπηση για αυτό τον τύπο αρχείου δεν υποστηρίζεται. Παρακαλώ κατεβάστε το αρχείο για να το δείτε.",
"share.modal.file-preview.error.not-supported.description": "Previews are not supported for this type of files. Please download the file to view it.",
// END /share/[id]
// /share/[id]/edit
"share.edit.title": "Ενημέρωση {shareId}",
@@ -292,62 +303,70 @@ export default {
"admin.config.general.app-name.description": "Ονομασία της εφαρμογής",
"admin.config.general.app-url": "URL Εφαρμογής",
"admin.config.general.app-url.description": "Η διεύθυνση URL όπου το Pingvin Share είναι διαθέσιμο",
"admin.config.general.secure-cookies": "Secure cookies",
"admin.config.general.secure-cookies.description": "Whether to set the secure flag on cookies. If enabled, the site will not function when accessed over HTTP.",
"admin.config.general.show-home-page": "Εμφάνιση αρχικής σελίδας",
"admin.config.general.show-home-page.description": "Εάν θα εμφανίζεται η αρχική σελίδα",
"admin.config.general.session-duration": "Session Duration",
"admin.config.general.session-duration.description": "Time in hours after which a user must log in again (default: 3 months).",
"admin.config.general.session-duration": "Διάρκεια συνεδρίας",
"admin.config.general.session-duration.description": "Χρόνος σε ώρες μετά την οποία ένας χρήστης πρέπει να συνδεθεί ξανά (προεπιλογή: 3 μήνες).",
"admin.config.general.logo": "Λογότυπο",
"admin.config.general.logo.description": "Αλλάξτε το λογότυπό σας ανεβάζοντας μια νέα εικόνα. Η εικόνα πρέπει να είναι PNG και αναλογία 1:1.",
"admin.config.general.logo.placeholder": "Επιλέξτε εικόνα",
"admin.config.email.enable-share-email-recipients": "Ενεργοποίηση κοινής χρήσης μέσω email",
"admin.config.email.enable-share-email-recipients.description": "Εάν θα ενεργοποιηθεί η κοινή χρήση μέσω email. Δυνατή μόνον με την ενεργοποίηση SMTP.",
"admin.config.email.enable-share-email-recipients": "Enable email recipient sharing",
"admin.config.email.enable-share-email-recipients.description": "Whether to allow email sharing with recipients. Only enable this if SMTP is activated.",
"admin.config.email.share-recipients-subject": "Θέμα στο email διαμοιρασμού",
"admin.config.email.share-recipients-subject.description": "Το θέμα του email διαμοιρασμού που θα φτάσει στον παραλήπτη.",
"admin.config.email.share-recipients-message": "Το θέμα του email για τον διαμοιρασμό που θα φτάσει στον παραλήπτη ",
"admin.config.email.share-recipients-message.description": "Μήνυμα που αποστέλλεται στους παραλήπτες κοινής χρήσης. Διαθέσιμες μεταβλητές:\n {creator} - Το όνομα χρήστη του δημιουργού της κοινής χρήσης\n {shareUrl} - Η διεύθυνση URL της κοινής χρήσης\n {desc} - Η περιγραφή της κοινής χρήσης\n {expires} - Η ημερομηνία λήξης της κοινής χρήσης\n Οι μεταβλητές θα αντικατασταθούν με την πραγματική τιμή.",
"admin.config.email.share-recipients-message.description": "Message which gets sent to the share recipients. Available variables:\n {creator} - The username of the creator of the share\n {creatorEmail} - The email of the creator of the share\n {shareUrl} - The URL of the share\n {desc} - The description of the share\n {expires} - The expiration date of the share\n These variables will be replaced with the actual value.",
"admin.config.email.reverse-share-subject": "Θέμα email αντίστροφου διαμοιρασμού",
"admin.config.email.reverse-share-subject.description": "Θέμα του ηλεκτρονικού ταχυδρομείου που αποστέλλεται όταν κάποιος δημιούργησε έναν αντίστροφο διαμοιρασμό.",
"admin.config.email.reverse-share-subject.description": "Subject of the sent email when someone created a share with your reverse share link.",
"admin.config.email.reverse-share-message": "Μήνυμα email αντίστροφου διαμοιρασμού",
"admin.config.email.reverse-share-message.description": "Μήνυμα που αποστέλλεται όταν κάποιος δημιουργεί έναν σύνδεσμο αντίστροφου διαμοιρασμού. Το {shareUrl} θα αντικατασταθεί με το όνομα του δημιουργού και τη διεύθυνση URL κοινής χρήσης.",
"admin.config.email.reset-password-subject": "Θέμα μηνύματος επαναφοράς κωδικού πρόσβασης",
"admin.config.email.reset-password-subject.description": "Θέμα του email που αποστέλλεται όταν ένας χρήστης ζητήσει επαναφορά κωδικού πρόσβασης.",
"admin.config.email.reset-password-subject.description": "Subject of the sent email when a user requests a password reset.",
"admin.config.email.reset-password-message": "Κείμενο μηνύματος επαναφοράς κωδικού πρόσβασης",
"admin.config.email.reset-password-message.description": "Μήνυμα που αποστέλλεται όταν ένας χρήστης ζητά επαναφορά κωδικού πρόσβασης. Το {url} θα αντικατασταθεί με τη διεύθυνση URL επαναφοράς κωδικού πρόσβασης.",
"admin.config.email.invite-subject": "Θέμα μηνύματος πρόσκλησης",
"admin.config.email.invite-subject.description": "Θέμα του email που αποστέλλεται όταν ένας διαχειριστής προσκαλεί έναν χρήστη.",
"admin.config.email.invite-subject.description": "Subject of the sent email when an admin invites a user.",
"admin.config.email.invite-message": "Μήνυμα μηνύματος πρόσκλησης",
"admin.config.email.invite-message.description": "Το μήνυμα που αποστέλλεται όταν ένας διαχειριστής προσκαλεί έναν χρήστη. Το {url} θα αντικατασταθεί με το URL πρόσκλησης και το {password} με τον κωδικό πρόσβασης.",
"admin.config.email.invite-message.description": "Message which gets sent when an admin invites a user. {url} will be replaced with the invite URL, {email} with the email and {password} with the users password.",
"admin.config.share.allow-registration": "Να επιτρέπεται η εγγραφή",
"admin.config.share.allow-registration.description": "Αν επιτρέπεται η εγγραφή",
"admin.config.share.allow-unauthenticated-shares": "Επιτρέψτε κοινές χρήσεις χωρίς έλεγχο ταυτότητας",
"admin.config.share.allow-unauthenticated-shares.description": "Εάν οι χρήστες χωρίς έλεγχο ταυτότητας μπορούν να δημιουργήσουν κοινόχρηστα στοιχεία",
"admin.config.share.max-expiration": "Μέγιστη λήξη",
"admin.config.share.max-expiration.description": "Μέγιστη λήξη κοινής χρήσης σε ώρες. Ορίστε το 0 για να επιτρέψετε απεριόριστη λήξη.",
"admin.config.share.share-id-length": "Default share ID length",
"admin.config.share.share-id-length.description": "Default length for the generated ID of a share. This value is also used to generate links for reverse shares. A value below 8 is not considered secure.",
"admin.config.share.max-size": "Μέγιστο μέγεθος",
"admin.config.share.max-size.description": "Μέγιστο μέγεθος κοινοποίησης σε bytes",
"admin.config.share.zip-compression-level": "Βαθμός συμπίεσης ZIP",
"admin.config.share.zip-compression-level.description": "Προσαρμόστε το βαθμό συμπίεσης για να εξισορροπηθεί το μέγεθος του αρχείου και η ταχύτητα επεξεργασίας. Έγκυρες τιμές κυμαίνονται από 0 έως 9, με 0 χωρίς συμπίεση και 9 μέγιστη συμπίεση.",
"admin.config.share.chunk-size": "Μέγεθος κομματιών",
"admin.config.share.chunk-size.description": "Προσαρμόστε το μέγεθος κομματιών (σε bytes) για να εξισορροπήσετε την αποδοτικότητα και την αξιοπιστία του συστήματος σύμφωνα με τη σύνδεσή σας στο διαδίκτυο. Μικρότερα κομμάτια μπορούν να βελτιώσουν τα ποσοστά επιτυχίας σε ασταθείς συνδέσεις, ενώ μεγαλύτερα κομμάτια επιταχύνουν τις μεταφορτώσεις σε σταθερές συνδέσεις.",
"admin.config.share.chunk-size.description": "Adjust the chunk size (in bytes) for your uploads to balance efficiency and reliability according to your internet connection. Smaller chunks can enhance success rates for unstable connections, while larger chunks make uploads faster for stable connections.",
"admin.config.share.auto-open-share-modal": "Auto open create share modal",
"admin.config.share.auto-open-share-modal.description": "The share creation modal automatically appears when a user selects files, eliminating the need to manually click the button.",
"admin.config.smtp.enabled": "Ενεργοποιημένο",
"admin.config.smtp.enabled": "Enable",
"admin.config.smtp.enabled.description": "Εάν η λειτουργία SMTP είναι ενεργοποιημένη. Ενεργοποιήστε τη μόνον όταν ορίσετε σωστά τις παραμέτρους που ακολουθούν.",
"admin.config.smtp.host": "Εξυπηρετητής",
"admin.config.smtp.host.description": "SMTP εξυπηρετητής",
"admin.config.smtp.port": "Θύρα",
"admin.config.smtp.port.description": "SMTP θύρα",
"admin.config.smtp.email": "Email",
"admin.config.smtp.email.description": "Διεύθυνση email από όπου αποστέλλονται τα μηνύματα",
"admin.config.smtp.email.description": "Email address from wich the emails get sent",
"admin.config.smtp.username": "Όνομα χρήστη",
"admin.config.smtp.username.description": "Όνομα χρήστη στον SMTP εξυπηρετητή",
"admin.config.smtp.password": "Κωδικός πρόσβασης",
"admin.config.smtp.password.description": "Κωδικός πρόσβασης στον εξυπηρετητή SMTP",
"admin.config.smtp.button.test": "Αποστολή δοκιμαστικού email",
"admin.config.smtp.allow-unauthorized-certificates": "Trust unauthorized SMTP server certificates",
"admin.config.smtp.allow-unauthorized-certificates.description": "Only set this to true if you need to trust self signed certificates.",
"admin.config.oauth.allow-registration": "Να επιτρέπεται η εγγραφή",
"admin.config.oauth.allow-registration.description": "Επιτρέψτε στους χρήστες να εγγραφούν μέσω λογαριασμών κοινωνικής δικτύωσης",
"admin.config.oauth.ignore-totp": "Παράβλεψη TOTP",
"admin.config.oauth.ignore-totp.description": "Αν θα αγνοηθεί το TOTP όταν ο χρήστης χρησιμοποιεί την κοινωνική σύνδεση",
"admin.config.oauth.disable-password": "Disable password login",
"admin.config.oauth.disable-password.description": "Whether to disable password login\nMake sure that an OAuth provider is properly configured before activating this configuration to avoid being locked out.",
"admin.config.oauth.github-enabled": "GitHub",
"admin.config.oauth.github-enabled.description": "Αν είναι ενεργοποιημένη η σύνδεση GitHub",
"admin.config.oauth.github-client-id": "GitHub Client ID",
@@ -370,6 +389,8 @@ export default {
"admin.config.oauth.microsoft-client-secret.description": "Μυστικό πελάτη της εφαρμογής Microsoft OAuth",
"admin.config.oauth.discord-enabled": "Discord",
"admin.config.oauth.discord-enabled.description": "Αν είναι ενεργοποιημένη η σύνδεση στο Discord",
"admin.config.oauth.discord-limited-users": "Discord limited users",
"admin.config.oauth.discord-limited-users.description": "Limit signing in to specific users by their Discord ID. Leave it blank to disable.",
"admin.config.oauth.discord-limited-guild": "Αναγνωριστικό διακομιστή περιορισμένης ισχύος Discord",
"admin.config.oauth.discord-limited-guild.description": "Περιορισμός σύνδεσης σε χρήστες σε ένα συγκεκριμένο διακομιστή. Αφήστε κενό για να απενεργοποιήσετε.",
"admin.config.oauth.discord-client-id": "Αναγνωριστικό Πελάτη Discord",
@@ -380,12 +401,41 @@ export default {
"admin.config.oauth.oidc-enabled.description": "Αν είναι ενεργοποιημένη η σύνδεση OpenID",
"admin.config.oauth.oidc-discovery-uri": "OpenID Connect Discovery URI",
"admin.config.oauth.oidc-discovery-uri.description": "Discovery URI of the OpenID Connect OAuth app",
"admin.config.oauth.oidc-sign-out": "Sign out from OpenID Connect",
"admin.config.oauth.oidc-sign-out.description": "Whether the “Sign out” button will sign out from the OpenID Connect provider",
"admin.config.oauth.oidc-scope": "OpenID Connect scope",
"admin.config.oauth.oidc-scope.description": "Scopes which should be requested from the OpenID Connect provider.",
"admin.config.oauth.oidc-username-claim": "OpenID Connect username claim",
"admin.config.oauth.oidc-username-claim.description": "Username claim in OpenID Connect ID token. Αφήστε κενό αν δε γνωρίζετε για αυτή τη ρύθμιση",
"admin.config.oauth.oidc-role-path": "Path to roles in OpenID Connect token",
"admin.config.oauth.oidc-role-path.description": "Must be a valid JMES path referencing an array of roles. " + "Managing access rights using OpenID Connect roles is only recommended if no other identity provider is configured and password login is disabled. " + "Leave it blank if you don't know what this config is.",
"admin.config.oauth.oidc-role-general-access": "OpenID Connect role for general access",
"admin.config.oauth.oidc-role-general-access.description": "Role required for general access. Must be present in a users roles for them to log in. " + "Leave it blank if you don't know what this config is.",
"admin.config.oauth.oidc-role-admin-access": "OpenID Connect role for admin access",
"admin.config.oauth.oidc-role-admin-access.description": "Role required for administrative access. Must be present in a users roles for them to access the admin panel. " + "Leave it blank if you don't know what this config is.",
"admin.config.oauth.oidc-client-id": "OpenID Connect Client ID",
"admin.config.oauth.oidc-client-id.description": "Client ID of the OpenID Connect OAuth app",
"admin.config.oauth.oidc-client-secret": "OpenID Connect Client secret",
"admin.config.oauth.oidc-client-secret.description": "Client secret of the OpenID Connect OAuth app",
"admin.config.category.ldap": "LDAP",
"admin.config.ldap.enabled": "Enable LDAP",
"admin.config.ldap.enabled.description": "Use LDAP authentication for user login",
"admin.config.ldap.url": "Server URL",
"admin.config.ldap.url.description": "URL of the LDAP server",
"admin.config.ldap.bind-dn": "Bind DN",
"admin.config.ldap.bind-dn.description": "Default user used to perform the user search",
"admin.config.ldap.bind-password": "Bind password",
"admin.config.ldap.bind-password.description": "Password used to perform the user search",
"admin.config.ldap.search-base": "User base",
"admin.config.ldap.search-base.description": "Base location, where the user search will be performed",
"admin.config.ldap.search-query": "User query",
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
"admin.config.ldap.admin-groups": "Admin group",
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
"admin.config.ldap.field-name-member-of": "User groups attribute name",
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
"admin.config.ldap.field-name-email": "User email attribute name",
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
// 404
"404.description": "Ουπς. Αυτή η σελίδα δεν υπάρχει.",
"404.button.home": "Πήγαινέ με πίσω",
@@ -400,10 +450,10 @@ export default {
"error.msg.no_user": "Ο χρήστης που συνδέεται με αυτόν τον λογαριασμό {0} δεν υπάρχει.",
"error.msg.no_email": "Δεν είναι δυνατή η λήψη διεύθυνσης ηλεκτρονικού ταχυδρομείου για αυτόν τον λογαριασμό {0}.",
"error.msg.already_linked": "Αυτός ο λογαριασμός {0} είναι ήδη συνδεδεμένος με άλλο λογαριασμό.",
"error.msg.not_linked": "Αυτός ο λογαριασμός {0} δεν έχει συνδεθεί με κανένα λογαριασμό ακόμα.",
"error.msg.not_linked": "This {0} account hasn't been linked to any account yet.",
"error.msg.unverified_account": "Αυτός ο λογαριασμός {0} δεν έχει επαληθευτεί, παρακαλώ προσπαθήστε ξανά μετά την επαλήθευση.",
"error.msg.discord_guild_permission_denied": "Δεν σας επιτρέπεται η σύνδεση.",
"error.msg.cannot_get_user_info": "Δεν είναι δυνατή η λήψη των πληροφοριών χρήστη από αυτόν τον λογαριασμό {0}.",
"error.msg.user_not_allowed": "Δεν σας επιτρέπεται η σύνδεση.",
"error.msg.cannot_get_user_info": "Cannot get your user info from this {0} account.",
"error.param.provider_github": "GitHub",
"error.param.provider_google": "Google",
"error.param.provider_microsoft": "Microsoft",
@@ -421,8 +471,9 @@ export default {
"common.button.generate": "Δημιουργία",
"common.button.done": "Ολοκληρώθηκε",
"common.text.link": "Σύνδεσμος",
"common.text.navigate-to-link": "Μεταβείτε στο σύνδεσμο",
"common.text.navigate-to-link": "Visit link",
"common.text.or": "ή",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "Επιστροφή",
"common.button.go-home": "Μετάβαση στην αρχική",
"common.notify.copied": "Ο σύνδεσμος σας αντιγράφηκε στο πρόχειρο",
@@ -432,6 +483,8 @@ export default {
"common.error.invalid-email": "Μη έγκυρη διεύθυνση e-mail",
"common.error.too-short": "Πρέπει να αποτελείται τουλάχιστον {length} χαρακτήρες",
"common.error.too-long": "Πρέπει να αποτελείται το πολύ από {length} χαρακτήρες",
"common.error.number-too-small": "Must be at least {min}",
"common.error.number-too-large": "Must be at most {max}",
"common.error.exact-length": "Πρέπει να αποτελείται ακριβώς από {length} χαρακτήρες",
"common.error.invalid-number": "Πρέπει να είναι αριθμός",
"common.error.field-required": "Αυτό το πεδίο είναι υποχρεωτικό"

View File

@@ -3,7 +3,7 @@ export default {
"navbar.upload": "Upload",
"navbar.signin": "Sign in",
"navbar.home": "Home",
"navbar.signup": "Sign Up",
"navbar.signup": "Sign up",
"navbar.links.shares": "My shares",
"navbar.links.reverse": "Reverse shares",
@@ -22,10 +22,10 @@ export default {
"home.bullet.a.description": "Host Pingvin Share on your own machine.",
"home.bullet.b.name": "Privacy",
"home.bullet.b.description":
"Your files are your files and should never get into the hands of third parties.",
"Your files are yours and will never be accessed by third parties.",
"home.bullet.c.name": "No annoying file size limit",
"home.bullet.c.description":
"Upload as big files as you want. Only your hard drive will be your limit.",
"Upload files as big as you want. Only your hard drive will be your limit.",
"home.button.start": "Get started",
"home.button.source": "Source code",
@@ -44,6 +44,7 @@ export default {
"signIn.notify.totp-required.description":
"Please enter your two-factor authentication code",
"signIn.oauth.or": "OR",
"signIn.oauth.signInWith": "Sign in with",
"signIn.oauth.github": "GitHub",
"signIn.oauth.google": "Google",
"signIn.oauth.microsoft": "Microsoft",
@@ -74,13 +75,13 @@ export default {
"resetPassword.title": "Forgot your password?",
"resetPassword.description": "Enter your email to reset your password.",
"resetPassword.notify.success":
"A message with a link to reset your password has been sent if the email exists.",
"A message with a link to reset your password has been sent if the provided email exists.",
"resetPassword.button.back": "Back to sign in page",
"resetPassword.text.resetPassword": "Reset password",
"resetPassword.text.enterNewPassword": "Enter your new password",
"resetPassword.input.password": "New password",
"resetPassword.notify.passwordReset":
"Your password has been reset successfully.",
"Your password has been successfully reset.",
// /account
"account.title": "My account",
@@ -94,7 +95,7 @@ export default {
"account.card.password.old": "Old password",
"account.card.password.new": "New password",
"account.card.password.noPasswordSet":
"You don't have a password set. If you want to sign in with email and password you need to set a password.",
"You do not have a password set. To sign in using your email and password, you need to create a password.",
"account.notify.password.success": "Password changed successfully",
"account.card.oauth.title": "Social login",
@@ -108,7 +109,7 @@ export default {
"account.card.oauth.unlinked": "Unlinked",
"account.modal.unlink.title": "Unlink account",
"account.modal.unlink.description":
"Unlinking your social accounts may cause you to lose your account if you don't remember your username and password.",
"Unlinking your social accounts may cause you to lose your account if you don't remember your login credentials",
"account.notify.oauth.unlinked.success": "Unlinked successfully",
"account.card.security.title": "Security",
@@ -154,14 +155,14 @@ export default {
"account.shares.table.name": "Name",
"account.shares.table.description": "Description",
"account.shares.table.visitors": "Visitors",
"account.shares.table.expiresAt": "Expires at",
"account.shares.table.createdAt": "Created at",
"account.shares.table.expiresAt": "Expires on",
"account.shares.table.createdAt": "Created on",
"account.shares.table.size": "Size",
"account.shares.modal.share-informations": "Share informations",
"account.shares.modal.share-link": "Share link",
"account.shares.modal.delete.title": "Delete share {share}",
"account.shares.modal.delete.title": "Delete share: {share}",
"account.shares.modal.delete.description":
"Do you really want to delete this share?",
@@ -194,9 +195,17 @@ export default {
"account.reverseShares.modal.max-size.label": "Max share size",
"account.reverseShares.modal.send-email": "Send email notification",
"account.reverseShares.modal.send-email": "Send email notifications",
"account.reverseShares.modal.send-email.description":
"Send an email notification when a share is created with this reverse share link.",
"Sends you an email notification when a share is created with this reverse share link.",
"account.reverseShares.modal.simplified": "Simple mode",
"account.reverseShares.modal.simplified.description":
"Make it easy for the person uploading the file to share it with you. They will only be able to customize the name and description of the share.",
"account.reverseShares.modal.public-access": "Public access",
"account.reverseShares.modal.public-access.description":
"Make the shares created with this reverse share public. If disabled, only you and the share creator will have access to view it.",
"account.reverseShares.modal.max-use.label": "Max uses",
"account.reverseShares.modal.max-use.description":
@@ -235,7 +244,7 @@ export default {
"admin.users.table.email": "Email",
"admin.users.table.admin": "Admin",
"admin.users.edit.update.title": "Update user {username}",
"admin.users.edit.update.title": "Edit user: {username}",
"admin.users.edit.update.admin-privileges": "Admin privileges",
"admin.users.edit.update.change-password.title": "Change password",
"admin.users.edit.update.change-password.field": "New password",
@@ -243,9 +252,9 @@ export default {
"admin.users.edit.update.notify.password.success":
"Password changed successfully",
"admin.users.edit.delete.title": "Delete user {username}",
"admin.users.edit.delete.title": "Delete user: {username} ?",
"admin.users.edit.delete.description":
"Do you really want to delete this user and all his shares?",
"Do you really want to delete this user and all their shares?",
// showCreateUserModal.tsx
"admin.users.modal.create.title": "Create user",
@@ -266,9 +275,9 @@ export default {
"admin.shares.table.id": "Share ID",
"admin.shares.table.username": "Creator",
"admin.shares.table.visitors": "Visitors",
"admin.shares.table.expires": "Expires At",
"admin.shares.table.expires": "Expires on",
"admin.shares.edit.delete.title": "Delete share {id}",
"admin.shares.edit.delete.title": "Delete share: {id}",
"admin.shares.edit.delete.description":
"Do you really want to delete this share?",
@@ -277,14 +286,19 @@ export default {
// /upload
"upload.title": "Upload",
"upload.notify.confirm-leave":
"Are you sure you want to leave this page? Your upload will be canceled.",
"upload.notify.generic-error":
"An error occurred while finishing your share.",
"upload.notify.count-failed": "{count} files failed to upload. Trying again.",
"upload.reverse-share.error.invalid.title": "Invalid reverse share link",
"upload.reverse-share.error.invalid.description":
"This reverse share has expired or is invalid.",
// Dropzone.tsx
"upload.dropzone.title": "Upload files",
"upload.dropzone.description":
"Drag'n'drop files here to start your share. We can accept only files that are less than {maxSize} in total.",
"Drag'n'drop files here to start your share. We only accept files up to {maxSize} in total.",
"upload.dropzone.notify.file-too-big":
"Your files exceed the maximum share size of {maxSize}.",
@@ -302,9 +316,9 @@ export default {
"You will be unable to delete your share manually and view the visitor count.",
"upload.modal.expires.never": "never",
"upload.modal.expires.never-long": "Never Expires",
"upload.modal.expires.never-long": "Permanent share",
"upload.modal.expires.error.too-long":
"Expiration exceeds maximum expiration date of {max}.",
"Expiration date exceeds the maximum of {max}.",
"upload.modal.link.label": "Link",
"upload.modal.expires.label": "Expiration",
@@ -341,6 +355,8 @@ export default {
"upload.modal.completed.expires-on":
"This share will expire on {expiration}.",
"upload.modal.completed.share-ready": "Share ready",
"upload.modal.completed.notified-reverse-share-creator":
"We have notified the creator of the reverse share. You can also manually share this link with them through other means.",
// END /upload
@@ -354,16 +370,19 @@ export default {
"share.error.not-found.title": "Share not found",
"share.error.not-found.description":
"The share you're looking for doesn't exist.",
"share.error.access-denied.title": "Private share",
"share.error.access-denied.description":
"The current account does not have permission to access this share",
"share.modal.password.title": "Password required",
"share.modal.password.description":
"To access this share please enter the password for the share.",
"Please enter the password to acces this share.",
"share.modal.password": "Password",
"share.modal.error.invalid-password": "Invalid password",
"share.button.download-all": "Download all",
"share.notify.download-all-preparing":
"The share is preparing. Try again in a few minutes.",
"The share is being prepared. Please try again in a few minutes.",
"share.modal.file-link": "File link",
"share.table.name": "Name",
@@ -371,7 +390,7 @@ export default {
"share.modal.file-preview.error.not-supported.title": "Preview not supported",
"share.modal.file-preview.error.not-supported.description":
"A preview for this file type is unsupported. Please download the file to view it.",
"Previews are not supported for this type of files. Please download the file to view it.",
// END /share/[id]
@@ -396,12 +415,14 @@ export default {
"admin.config.general.app-url": "App URL",
"admin.config.general.app-url.description":
"On which URL Pingvin Share is available",
"admin.config.general.secure-cookies": "Secure cookies",
"admin.config.general.secure-cookies.description":
"Whether to set the secure flag on cookies. If enabled, the site will not function when accessed over HTTP.",
"admin.config.general.show-home-page": "Show home page",
"admin.config.general.show-home-page.description":
"Whether to show the home page",
"admin.config.general.session-duration":
"Session Duration",
"admin.config.general.session-duration.description":
"admin.config.general.session-duration": "Session Duration",
"admin.config.general.session-duration.description":
"Time in hours after which a user must log in again (default: 3 months).",
"admin.config.general.logo": "Logo",
"admin.config.general.logo.description":
@@ -409,33 +430,33 @@ export default {
"admin.config.general.logo.placeholder": "Pick image",
"admin.config.email.enable-share-email-recipients":
"Enable share email recipients",
"Enable email recipient sharing",
"admin.config.email.enable-share-email-recipients.description":
"Whether to allow emails to share recipients. Only enable this if you have enabled SMTP.",
"Whether to allow email sharing with recipients. Only enable this if SMTP is activated.",
"admin.config.email.share-recipients-subject": "Share recipients subject",
"admin.config.email.share-recipients-subject.description":
"Subject of the email which gets sent to the share recipients.",
"admin.config.email.share-recipients-message": "Share recipients message",
"admin.config.email.share-recipients-message.description":
"Message which gets sent to the share recipients. Available variables:\n {creator} - The username of the creator of the share\n {shareUrl} - The URL of the share\n {desc} - The description of the share\n {expires} - The expiration date of the share\n The variables will be replaced with the actual value.",
"Message which gets sent to the share recipients. Available variables:\n {creator} - The username of the creator of the share\n {creatorEmail} - The email of the creator of the share\n {shareUrl} - The URL of the share\n {desc} - The description of the share\n {expires} - The expiration date of the share\n These variables will be replaced with the actual value.",
"admin.config.email.reverse-share-subject": "Reverse share subject",
"admin.config.email.reverse-share-subject.description":
"Subject of the email which gets sent when someone created a share with your reverse share link.",
"Subject of the sent email when someone created a share with your reverse share link.",
"admin.config.email.reverse-share-message": "Reverse share message",
"admin.config.email.reverse-share-message.description":
"Message which gets sent when someone created a share with your reverse share link. {shareUrl} will be replaced with the creator's name and the share URL.",
"admin.config.email.reset-password-subject": "Reset password subject",
"admin.config.email.reset-password-subject.description":
"Subject of the email which gets sent when a user requests a password reset.",
"Subject of the sent email when a user requests a password reset.",
"admin.config.email.reset-password-message": "Reset password message",
"admin.config.email.reset-password-message.description":
"Message which gets sent when a user requests a password reset. {url} will be replaced with the reset password URL.",
"admin.config.email.invite-subject": "Invite subject",
"admin.config.email.invite-subject.description":
"Subject of the email which gets sent when an admin invites a user.",
"Subject of the sent email when an admin invites a user.",
"admin.config.email.invite-message": "Invite message",
"admin.config.email.invite-message.description":
"Message which gets sent when an admin invites a user. {url} will be replaced with the invite URL and {password} with the password.",
"Message which gets sent when an admin invites a user. {url} will be replaced with the invite URL, {email} with the email and {password} with the users password.",
"admin.config.share.allow-registration": "Allow registration",
"admin.config.share.allow-registration.description":
@@ -447,17 +468,22 @@ export default {
"admin.config.share.max-expiration": "Max expiration",
"admin.config.share.max-expiration.description":
"Maximum share expiration in hours. Set to 0 to allow unlimited expiration.",
"admin.config.share.share-id-length": "Default share ID length",
"admin.config.share.share-id-length.description":
"Default length for the generated ID of a share. This value is also used to generate links for reverse shares. A value below 8 is not considered secure.",
"admin.config.share.max-size": "Max size",
"admin.config.share.max-size.description": "Maximum share size in bytes",
"admin.config.share.zip-compression-level": "Zip compression level",
"admin.config.share.zip-compression-level.description":
"Adjust the level to balance between file size and compression speed. Valid values range from 0 to 9, with 0 being no compression and 9 being maximum compression. ",
"admin.config.share.chunk-size": "Chunk size",
"admin.config.share.chunk-size.description": "Adjust the chunk size (in bytes) for your uploads to balance efficiency and reliability according to your internet connection. Smaller chunks can enhance success rates for unstable connections, while larger chunks speed up uploads for stable connections.",
"admin.config.share.chunk-size.description":
"Adjust the chunk size (in bytes) for your uploads to balance efficiency and reliability according to your internet connection. Smaller chunks can enhance success rates for unstable connections, while larger chunks make uploads faster for stable connections.",
"admin.config.share.auto-open-share-modal": "Auto open create share modal",
"admin.config.share.auto-open-share-modal.description": "The share creation modal automatically appears when a user selects files, eliminating the need to manually click the button.",
"admin.config.share.auto-open-share-modal.description":
"The share creation modal automatically appears when a user selects files, eliminating the need to manually click the button.",
"admin.config.smtp.enabled": "Enabled",
"admin.config.smtp.enabled": "Enable",
"admin.config.smtp.enabled.description":
"Whether SMTP is enabled. Only set this to true if you entered the host, port, email, user and password of your SMTP server.",
"admin.config.smtp.host": "Host",
@@ -466,12 +492,16 @@ export default {
"admin.config.smtp.port.description": "Port of the SMTP server",
"admin.config.smtp.email": "Email",
"admin.config.smtp.email.description":
"Email address which the emails get sent from",
"Email address from wich the emails get sent",
"admin.config.smtp.username": "Username",
"admin.config.smtp.username.description": "Username of the SMTP server",
"admin.config.smtp.password": "Password",
"admin.config.smtp.password.description": "Password of the SMTP server",
"admin.config.smtp.button.test": "Send test email",
"admin.config.smtp.allow-unauthorized-certificates":
"Trust unauthorized SMTP server certificates",
"admin.config.smtp.allow-unauthorized-certificates.description":
"Only set this to true if you need to trust self signed certificates.",
"admin.config.oauth.allow-registration": "Allow registration",
"admin.config.oauth.allow-registration.description":
@@ -479,6 +509,9 @@ export default {
"admin.config.oauth.ignore-totp": "Ignore TOTP",
"admin.config.oauth.ignore-totp.description":
"Whether to ignore TOTP when user using social login",
"admin.config.oauth.disable-password": "Disable password login",
"admin.config.oauth.disable-password.description":
"Whether to disable password login\nMake sure that an OAuth provider is properly configured before activating this configuration to avoid being locked out.",
"admin.config.oauth.github-enabled": "GitHub",
"admin.config.oauth.github-enabled.description":
"Whether GitHub login is enabled",
@@ -512,6 +545,9 @@ export default {
"admin.config.oauth.discord-enabled": "Discord",
"admin.config.oauth.discord-enabled.description":
"Whether Discord login is enabled",
"admin.config.oauth.discord-limited-users": "Discord limited users",
"admin.config.oauth.discord-limited-users.description":
"Limit signing in to specific users by their Discord ID. Leave it blank to disable.",
"admin.config.oauth.discord-limited-guild": "Discord limited server ID",
"admin.config.oauth.discord-limited-guild.description":
"Limit signing in to users in a specific server. Leave it blank to disable.",
@@ -527,9 +563,30 @@ export default {
"admin.config.oauth.oidc-discovery-uri": "OpenID Connect Discovery URI",
"admin.config.oauth.oidc-discovery-uri.description":
"Discovery URI of the OpenID Connect OAuth app",
"admin.config.oauth.oidc-sign-out": "Sign out from OpenID Connect",
"admin.config.oauth.oidc-sign-out.description":
"Whether the “Sign out” button will sign out from the OpenID Connect provider",
"admin.config.oauth.oidc-scope": "OpenID Connect scope",
"admin.config.oauth.oidc-scope.description":
"Scopes which should be requested from the OpenID Connect provider.",
"admin.config.oauth.oidc-username-claim": "OpenID Connect username claim",
"admin.config.oauth.oidc-username-claim.description":
"Username claim in OpenID Connect ID token. Leave it blank if you don't know what this config is.",
"admin.config.oauth.oidc-role-path": "Path to roles in OpenID Connect token",
"admin.config.oauth.oidc-role-path.description":
"Must be a valid JMES path referencing an array of roles. " +
"Managing access rights using OpenID Connect roles is only recommended if no other identity provider is configured and password login is disabled. " +
"Leave it blank if you don't know what this config is.",
"admin.config.oauth.oidc-role-general-access":
"OpenID Connect role for general access",
"admin.config.oauth.oidc-role-general-access.description":
"Role required for general access. Must be present in a users roles for them to log in. " +
"Leave it blank if you don't know what this config is.",
"admin.config.oauth.oidc-role-admin-access":
"OpenID Connect role for admin access",
"admin.config.oauth.oidc-role-admin-access.description":
"Role required for administrative access. Must be present in a users roles for them to access the admin panel. " +
"Leave it blank if you don't know what this config is.",
"admin.config.oauth.oidc-client-id": "OpenID Connect Client ID",
"admin.config.oauth.oidc-client-id.description":
"Client ID of the OpenID Connect OAuth app",
@@ -537,6 +594,34 @@ export default {
"admin.config.oauth.oidc-client-secret.description":
"Client secret of the OpenID Connect OAuth app",
"admin.config.category.ldap": "LDAP",
"admin.config.ldap.enabled": "Enable LDAP",
"admin.config.ldap.enabled.description":
"Use LDAP authentication for user login",
"admin.config.ldap.url": "Server URL",
"admin.config.ldap.url.description": "URL of the LDAP server",
"admin.config.ldap.bind-dn": "Bind DN",
"admin.config.ldap.bind-dn.description":
"Default user used to perform the user search",
"admin.config.ldap.bind-password": "Bind password",
"admin.config.ldap.bind-password.description":
"Password used to perform the user search",
"admin.config.ldap.search-base": "User base",
"admin.config.ldap.search-base.description":
"Base location, where the user search will be performed",
"admin.config.ldap.search-query": "User query",
"admin.config.ldap.search-query.description":
"The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
"admin.config.ldap.admin-groups": "Admin group",
"admin.config.ldap.admin-groups.description":
"Group required for administrative access.",
"admin.config.ldap.field-name-member-of": "User groups attribute name",
"admin.config.ldap.field-name-member-of.description":
"LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
"admin.config.ldap.field-name-email": "User email attribute name",
"admin.config.ldap.field-name-email.description":
"LDAP attribute name for the email of an user.",
// 404
"404.description": "Oops this page doesn't exist.",
"404.button.home": "Bring me back home",
@@ -555,13 +640,13 @@ export default {
"error.msg.no_email": "Can't get email address from this {0} account.",
"error.msg.already_linked":
"This {0} account is already linked to another account.",
"error.msg.not_linked": "This {0} account haven't linked to any account yet.",
"error.msg.not_linked":
"This {0} account hasn't been linked to any account yet.",
"error.msg.unverified_account":
"This {0} account is unverified, please try again after verification.",
"error.msg.discord_guild_permission_denied":
"You are not allowed to sign in.",
"error.msg.user_not_allowed": "You are not allowed to sign in.",
"error.msg.cannot_get_user_info":
"Can not get your user info from this {0} account.",
"Cannot get your user info from this {0} account.",
"error.param.provider_github": "GitHub",
"error.param.provider_google": "Google",
"error.param.provider_microsoft": "Microsoft",
@@ -580,8 +665,9 @@ export default {
"common.button.generate": "Generate",
"common.button.done": "Done",
"common.text.link": "Link",
"common.text.navigate-to-link": "Go to the link",
"common.text.navigate-to-link": "Visit link",
"common.text.or": "or",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "Go back",
"common.button.go-home": "Go home",
"common.notify.copied": "Your link was copied to the clipboard",
@@ -592,6 +678,8 @@ export default {
"common.error.invalid-email": "Invalid email address",
"common.error.too-short": "Must be at least {length} characters",
"common.error.too-long": "Must be at most {length} characters",
"common.error.number-too-small": "Must be at least {min}",
"common.error.number-too-large": "Must be at most {max}",
"common.error.exact-length": "Must be exactly {length} characters",
"common.error.invalid-number": "Must be a number",
"common.error.field-required": "This field is required",

View File

@@ -16,9 +16,9 @@ export default {
"home.bullet.a.name": "Autoalojada",
"home.bullet.a.description": "Aloja Pingvin Share en tu propio equipo.",
"home.bullet.b.name": "Privacidad",
"home.bullet.b.description": "Tus archivos son tus archivos y nunca deberían terminar en manos de terceros.",
"home.bullet.b.description": "Tus archivos son tuyos y nunca serán accesibles por terceros.",
"home.bullet.c.name": "Sin molestos límites de tamaño de archivo",
"home.bullet.c.description": "Sube archivos tan grandes como quieras. El único límite es la capacidad de tu disco duro.",
"home.bullet.c.description": "Sube archivos del tamaño que desees. Solo el espacio en tu disco duro será tu límite.",
"home.button.start": "Comenzar",
"home.button.source": "Código fuente",
// END /
@@ -34,6 +34,7 @@ export default {
"signIn.notify.totp-required.title": "Se requiere autenticación de dos factores",
"signIn.notify.totp-required.description": "Por favor ingrese su código de autenticación de dos factores",
"signIn.oauth.or": "O",
"signIn.oauth.signInWith": "Iniciar sesión con",
"signIn.oauth.github": "GitHub",
"signIn.oauth.google": "Google",
"signIn.oauth.microsoft": "Microsoft",
@@ -57,12 +58,12 @@ export default {
// /auth/reset-password
"resetPassword.title": "¿Olvidaste tu contraseña?",
"resetPassword.description": "Ingresa tu correo para restablecer tu contraseña.",
"resetPassword.notify.success": "Se ha enviado un mensaje con un enlace para restablecer tu contraseña.",
"resetPassword.notify.success": "Se ha enviado un mensaje con un enlace para restablecer tu contraseña si el correo electrónico proporcionado existe.",
"resetPassword.button.back": "Volver al inicio de sesión",
"resetPassword.text.resetPassword": "Restablecer contraseña",
"resetPassword.text.enterNewPassword": "Ingresa tu nueva contraseña",
"resetPassword.input.password": "Nueva contraseña",
"resetPassword.notify.passwordReset": "Tu contraseña se ha restablecido correctamente.",
"resetPassword.notify.passwordReset": "Tu contraseña ha sido restablecida exitosamente.",
// /account
"account.title": "Mi cuenta",
"account.card.info.title": "Información de cuenta",
@@ -72,7 +73,7 @@ export default {
"account.card.password.title": "Contraseña",
"account.card.password.old": "Anterior contraseña",
"account.card.password.new": "Nueva contraseña",
"account.card.password.noPasswordSet": "No tienes una establecida contraseña. Si deseas iniciar sesión con correo electrónico y una contraseña necesitas crear una contraseña.",
"account.card.password.noPasswordSet": "No tienes una contraseña configurada. Para iniciar sesión usando tu correo electrónico y contraseña, necesitas crear una contraseña.",
"account.notify.password.success": "Contraseña cambiada correctamente",
"account.card.oauth.title": "Inicio de sesión con red social",
"account.card.oauth.github": "GitHub",
@@ -84,7 +85,7 @@ export default {
"account.card.oauth.unlink": "Desvincular",
"account.card.oauth.unlinked": "Desvinculado",
"account.modal.unlink.title": "Desvincular cuenta",
"account.modal.unlink.description": "Desvincular tus cuentas sociales puede causar que pierdas tu cuenta si no recuerdas tu nombre de usuario y contraseña.",
"account.modal.unlink.description": "Desvincular tus cuentas sociales puede hacer que pierdas acceso a tu cuenta si no recuerdas tus credenciales de inicio de sesión",
"account.notify.oauth.unlinked.success": "Desvinculado correctamente",
"account.card.security.title": "Seguridad",
"account.card.security.totp.enable.description": "Ingrese su contraseña actual para habilitar TOTP",
@@ -120,12 +121,12 @@ export default {
"account.shares.table.name": "Nombre",
"account.shares.table.description": "Descripción",
"account.shares.table.visitors": "Visitas",
"account.shares.table.expiresAt": "Expira en",
"account.shares.table.createdAt": "Creado en",
"account.shares.table.expiresAt": "Vence el",
"account.shares.table.createdAt": "Creado el",
"account.shares.table.size": "Tamaño",
"account.shares.modal.share-informations": "Información del compartido",
"account.shares.modal.share-link": "Enlace",
"account.shares.modal.delete.title": "Eliminar compartido {share}",
"account.shares.modal.delete.title": "Eliminar enlace compartido: {share}",
"account.shares.modal.delete.description": "¿Seguro que quieres eliminar este compartido?",
// END /account/shares
// /account/reverseShares
@@ -149,8 +150,12 @@ export default {
"account.reverseShares.modal.expiration.year-singular": "Año",
"account.reverseShares.modal.expiration.year-plural": "Años",
"account.reverseShares.modal.max-size.label": "Tamaño máximo del compartido",
"account.reverseShares.modal.send-email": "Enviar notificación por correo",
"account.reverseShares.modal.send-email.description": "Enviar una notificación por correo cuando se comparta algo con este enlace de compartición inversa.",
"account.reverseShares.modal.send-email": "Enviar notificaciones por correo electrónico",
"account.reverseShares.modal.send-email.description": "Te envía una notificación por correo electrónico cuando se crea un enlace compartido con este enlace de compartición inverso.",
"account.reverseShares.modal.simplified": "Modo simple",
"account.reverseShares.modal.simplified.description": "Facilita que la persona que sube el archivo lo comparta contigo. Solo podrá personalizar el nombre y la descripción del enlace compartido.",
"account.reverseShares.modal.public-access": "Acceso público",
"account.reverseShares.modal.public-access.description": "Haz que los enlaces compartidos creados con este enlace de compartición inverso sean públicos. Si está desactivado, solo tú y el creador del enlace tendrán acceso para verlo.",
"account.reverseShares.modal.max-use.label": "Máximo de usos",
"account.reverseShares.modal.max-use.description": "Cantidad máxima de veces que esta URL se puede usar para crear un compartido.",
"account.reverseShare.never-expires": "Esta compartición inversa nunca expirará.",
@@ -169,7 +174,7 @@ export default {
// /admin
"admin.title": "Administración",
"admin.button.users": "Gestión de usuarios",
"admin.button.shares": "Share management",
"admin.button.shares": "Gestión de comparticiones",
"admin.button.config": "Configuración",
"admin.version": "Versión",
// END /admin
@@ -178,14 +183,14 @@ export default {
"admin.users.table.username": "Nombre de usuario",
"admin.users.table.email": "Correo",
"admin.users.table.admin": "Administrador",
"admin.users.edit.update.title": "Actualizar usuario {username}",
"admin.users.edit.update.title": "Editar usuario: {username}",
"admin.users.edit.update.admin-privileges": "Privilegios de administrador",
"admin.users.edit.update.change-password.title": "Cambiar contraseña",
"admin.users.edit.update.change-password.field": "Nueva contraseña",
"admin.users.edit.update.change-password.button": "Guardar nueva contraseña",
"admin.users.edit.update.notify.password.success": "Contraseña cambiada correctamente",
"admin.users.edit.delete.title": "Eliminar usuario {username}",
"admin.users.edit.delete.description": "¿Realmente quiere eliminar este usuario y todos sus archivos compartidos?",
"admin.users.edit.delete.title": "Eliminar usuario: {username} ?",
"admin.users.edit.delete.description": "¿Realmente deseas eliminar a este usuario y todos sus enlaces compartidos?",
// showCreateUserModal.tsx
"admin.users.modal.create.title": "Crear usuario",
"admin.users.modal.create.username": "Nombre de usuario",
@@ -197,21 +202,24 @@ export default {
"admin.users.modal.create.admin.description": "Si se marca, el usuario podrá acceder al panel de administrador.",
// END /admin/users
// /admin/shares
"admin.shares.title": "Share management",
"admin.shares.table.id": "Share ID",
"admin.shares.table.username": "Creator",
"admin.shares.table.visitors": "Visitors",
"admin.shares.table.expires": "Expires At",
"admin.shares.edit.delete.title": "Delete share {id}",
"admin.shares.edit.delete.description": "Do you really want to delete this share?",
"admin.shares.title": "Gestión de comparticiones",
"admin.shares.table.id": "ID de compartición",
"admin.shares.table.username": "Creador",
"admin.shares.table.visitors": "Visitantes",
"admin.shares.table.expires": "Vence el",
"admin.shares.edit.delete.title": "Eliminar enlace compartido: {id}",
"admin.shares.edit.delete.description": "¿Seguro que quieres eliminar este compartido?",
// END /admin/shares
// /upload
"upload.title": "Subir",
"upload.notify.confirm-leave": "Are you sure you want to leave this page? Your upload will be canceled.",
"upload.notify.generic-error": "Ha ocurrido un error mientras se compartía tu archivo.",
"upload.notify.count-failed": "No se pudo cargar {count} archivos. Intentando nuevamente.",
"upload.reverse-share.error.invalid.title": "Invalid reverse share link",
"upload.reverse-share.error.invalid.description": "This reverse share has expired or is invalid.",
// Dropzone.tsx
"upload.dropzone.title": "Subir archivos",
"upload.dropzone.description": "Arrastra archivos aquí para comenzar a compartir. Aceptamos archivos de un tamaño menor a {maxSize} en total.",
"upload.dropzone.description": "Arrastra y suelta los archivos aquí para crear tu enlace compartido. Solo aceptamos archivos de hasta {maxSize} en total.",
"upload.dropzone.notify.file-too-big": "Tus archivos exceden el tamaño máximo de {maxSize}.",
// FileList.tsx
"upload.filelist.name": "Nombre",
@@ -223,8 +231,8 @@ export default {
"upload.modal.not-signed-in": "No has iniciado sesión",
"upload.modal.not-signed-in-description": "No podrás eliminar tus compartidos manualmente ni ver el número de visitas.",
"upload.modal.expires.never": "nunca",
"upload.modal.expires.never-long": "Nunca Expira",
"upload.modal.expires.error.too-long": "La caducidad excede la fecha de caducidad máxima de {max}.",
"upload.modal.expires.never-long": "Enlace compartido permanente",
"upload.modal.expires.error.too-long": "La fecha de expiración excede el máximo de {max}.",
"upload.modal.link.label": "Enlace",
"upload.modal.expires.label": "Expiración",
"upload.modal.expires.minute-singular": "Minuto",
@@ -254,6 +262,7 @@ export default {
"upload.modal.completed.never-expires": "Este compartido nunca expirará.",
"upload.modal.completed.expires-on": "Este compartido expira en {expiration}.",
"upload.modal.completed.share-ready": "Compartido listo",
"upload.modal.completed.notified-reverse-share-creator": "Hemos notificado al creador de la compartición inversa. También puedes compartir manualmente este enlace con otros a través de otros medios.",
// END /upload
// /share/[id]
"share.title": "Compartido {shareId}",
@@ -263,17 +272,19 @@ export default {
"share.error.removed.title": "Compartido eliminado",
"share.error.not-found.title": "Compartido no encontrado",
"share.error.not-found.description": "El compartido que estás buscando no existe.",
"share.error.access-denied.title": "Compartición privada",
"share.error.access-denied.description": "La cuenta actual no tiene permiso para acceder a este compartido",
"share.modal.password.title": "Se requiere contraseña",
"share.modal.password.description": "Por favor ingrese la contraseña para poder acceder a este compartido.",
"share.modal.password.description": "Por favor, ingresa la contraseña para acceder a este enlace compartido.",
"share.modal.password": "Contraseña",
"share.modal.error.invalid-password": "Contraseña inválida",
"share.button.download-all": "Descargar todo",
"share.notify.download-all-preparing": "Se está preparando el compartido. Intente de nuevo en unos minutos.",
"share.notify.download-all-preparing": "El enlace compartido está en preparación. Por favor, inténtalo de nuevo en unos minutos.",
"share.modal.file-link": "Enlace del archivo",
"share.table.name": "Nombre",
"share.table.size": "Tamaño",
"share.modal.file-preview.error.not-supported.title": "Vista previa no disponible",
"share.modal.file-preview.error.not-supported.description": "La vista previa para este tipo de archivo no está disponible. Por favor descargue el archivo para verlo.",
"share.modal.file-preview.error.not-supported.description": "Las vistas previas no son compatibles con este tipo de archivos. Por favor, descargue el archivo para verlo.",
// END /share/[id]
// /share/[id]/edit
"share.edit.title": "Editar {shareId}",
@@ -292,62 +303,70 @@ export default {
"admin.config.general.app-name.description": "Nombre de la aplicación",
"admin.config.general.app-url": "App URL",
"admin.config.general.app-url.description": "En cuál URL está disponible Pingvin Share",
"admin.config.general.secure-cookies": "Secure cookies",
"admin.config.general.secure-cookies.description": "Whether to set the secure flag on cookies. If enabled, the site will not function when accessed over HTTP.",
"admin.config.general.show-home-page": "Mostrar página de inicio",
"admin.config.general.show-home-page.description": "Mostrar o no la página de inicio",
"admin.config.general.session-duration": "Session Duration",
"admin.config.general.session-duration.description": "Time in hours after which a user must log in again (default: 3 months).",
"admin.config.general.session-duration": "Duración de la sesión",
"admin.config.general.session-duration.description": "Tiempo en horas después del cual un usuario debe iniciar sesión de nuevo (por defecto: 3 meses).",
"admin.config.general.logo": "Logo",
"admin.config.general.logo.description": "Cambia tu logo subiendo una nueva imagen. La imagen debe ser un PNG y debe estar en formato 1:1.",
"admin.config.general.logo.placeholder": "Elegir imagen",
"admin.config.email.enable-share-email-recipients": "Activar destinatarios por correo",
"admin.config.email.enable-share-email-recipients.description": "Si desea permitir a los destinatarios compartir por correo. Activa esto solo si tienes habilitado SMTP.",
"admin.config.email.enable-share-email-recipients": "Habilitar compartir por correo electrónico",
"admin.config.email.enable-share-email-recipients.description": "Si desea permitir compartir por correo electrónico. Solo habilita esto si SMTP está activado.",
"admin.config.email.share-recipients-subject": "Asunto destinatario",
"admin.config.email.share-recipients-subject.description": "Asunto del correo el cual es enviado al destinatario del compartido.",
"admin.config.email.share-recipients-message": "Mensaje destinatario",
"admin.config.email.share-recipients-message.description": "Mensaje el cual es enviado al destinatario del compartido. Variables disponibles:\n{creator} - Nombre del creador del compartido\n {shareUrl} - URL del compartido\n {desc} - Descripción del compartido\n {expires} - Fecha de expiración del compartido\nLas variables serán remplazadas con los valores reales.",
"admin.config.email.share-recipients-message.description": "Message which gets sent to the share recipients. Available variables:\n {creator} - The username of the creator of the share\n {creatorEmail} - The email of the creator of the share\n {shareUrl} - The URL of the share\n {desc} - The description of the share\n {expires} - The expiration date of the share\n These variables will be replaced with the actual value.",
"admin.config.email.reverse-share-subject": "Asunto de la compartición inversa",
"admin.config.email.reverse-share-subject.description": "Asunto del correo el cual se envía cuando alguien comparte algo con tu enlace de compartición inversa.",
"admin.config.email.reverse-share-subject.description": "Asunto del correo electrónico enviado cuando alguien crea un enlace compartido con tu enlace compartido inverso.",
"admin.config.email.reverse-share-message": "Mensaje de la compartición inversa",
"admin.config.email.reverse-share-message.description": "Mensaje que se envía cuando alguien comparte algo con tu enlace de compartición inversa. {shareUrl} Se remplazará con el nombre del creador y la URL del compartido.",
"admin.config.email.reset-password-subject": "Asunto restablecer contraseña",
"admin.config.email.reset-password-subject.description": "Asunto del correo que se envía cuando un usuario solicita restablecer la contraseña.",
"admin.config.email.reset-password-subject.description": "Asunto del correo electrónico enviado cuando un usuario solicita un restablecimiento de contraseña.",
"admin.config.email.reset-password-message": "Mensaje restablecer contraseña",
"admin.config.email.reset-password-message.description": "Mensaje que se envía cuando un usuario solicita restablecer la contraseña. {url} se remplazará con la URL para restablecer la contraseña.",
"admin.config.email.invite-subject": "Asunto de la invitación",
"admin.config.email.invite-subject.description": "Asunto del correo que se envía cuando un administrador invita a un usuario.",
"admin.config.email.invite-subject.description": "Asunto del correo electrónico enviado cuando un administrador invita a un usuario.",
"admin.config.email.invite-message": "Mensaje de invitación",
"admin.config.email.invite-message.description": "Mensaje que se envía cuando un administrador invita a un usuario. {url} Se remplazará con la URL de invitación y {password} con la contraseña.",
"admin.config.email.invite-message.description": "Mensaje que se envía cuando un administrador invita a un usuario. {url} se reemplazará con la URL de invitación, {email} con el correo electrónico y {password} con la contraseña del usuario.",
"admin.config.share.allow-registration": "Permitir registro",
"admin.config.share.allow-registration.description": "Si se permite el registro",
"admin.config.share.allow-unauthenticated-shares": "Permitir compartir sin iniciar sesión",
"admin.config.share.allow-unauthenticated-shares.description": "Si los usuarios que no han iniciado sesión pueden compartir",
"admin.config.share.max-expiration": "Expiración máxima",
"admin.config.share.max-expiration.description": "Expiración máxima para compartir en horas. Establezca en 0 para permitir una expiración ilimitada.",
"admin.config.share.share-id-length": "Default share ID length",
"admin.config.share.share-id-length.description": "Default length for the generated ID of a share. This value is also used to generate links for reverse shares. A value below 8 is not considered secure.",
"admin.config.share.max-size": "Tamaño máximo",
"admin.config.share.max-size.description": "Tamaño máximo de los archivos, en bytes",
"admin.config.share.zip-compression-level": "Nivel de compresión del Zip",
"admin.config.share.zip-compression-level.description": "Ajustar el nivel para equilibrar entre el tamaño del archivo y la velocidad de compresión. Los valores válidos van del 0 al 9, siendo 0 sin compresión y 9 el nivel máximo de compresión. ",
"admin.config.share.chunk-size": "Tamaño de los fragmentos",
"admin.config.share.chunk-size.description": "Ajusta el tamaño de los fragmentos (en bytes) para tus cargas para equilibrar la eficiencia y la fiabilidad según tu conexión a internet. Fragmentos más pequeños pueden mejorar las tasas de éxito para conexiones inestables, mientras que fragmentos más grandes aceleran las cargas para conexiones estables.",
"admin.config.share.auto-open-share-modal": "Auto open create share modal",
"admin.config.share.auto-open-share-modal.description": "The share creation modal automatically appears when a user selects files, eliminating the need to manually click the button.",
"admin.config.smtp.enabled": "Habilitado",
"admin.config.share.chunk-size.description": "Ajusta el tamaño del fragmento (en bytes) para tus subidas y equilibra la eficiencia y la fiabilidad según tu conexión a Internet. Fragmentos más pequeños pueden aumentar las tasas de éxito para conexiones inestables, mientras que fragmentos más grandes hacen que las subidas sean más rápidas para conexiones estables.",
"admin.config.share.auto-open-share-modal": "Auto abrir un modal de creación de compartidos",
"admin.config.share.auto-open-share-modal.description": "El modal de creación de compartir aparece automáticamente cuando un usuario selecciona archivos, eliminando la necesidad de hacer clic manualmente en el botón.",
"admin.config.smtp.enabled": "Habilitar",
"admin.config.smtp.enabled.description": "Si SMTP está habilitado. Active solo si ha introducido el host, el puerto, el correo, el usuario y la contraseña de su servidor SMTP.",
"admin.config.smtp.host": "Host",
"admin.config.smtp.host.description": "Host del servidor SMTP",
"admin.config.smtp.port": "Puerto",
"admin.config.smtp.port.description": "Puerto del servidor SMTP",
"admin.config.smtp.email": "Correo",
"admin.config.smtp.email.description": "Dirección de correo desde la cual se envían los correos",
"admin.config.smtp.email.description": "Dirección desde la cual se envían los correos electrónicos",
"admin.config.smtp.username": "Usuario",
"admin.config.smtp.username.description": "Usuario del servidor SMTP",
"admin.config.smtp.password": "Contraseña",
"admin.config.smtp.password.description": "Contraseña del servidor SMTP",
"admin.config.smtp.button.test": "Enviar correo de prueba",
"admin.config.smtp.allow-unauthorized-certificates": "Confiar en certificados de servidor SMTP no autorizados",
"admin.config.smtp.allow-unauthorized-certificates.description": "Sólo establece esto como verdadero si necesitas confiar en certificados autofirmados.",
"admin.config.oauth.allow-registration": "Permitir registro",
"admin.config.oauth.allow-registration.description": "Permitir a los usuarios registrarse mediante login social",
"admin.config.oauth.ignore-totp": "Ignorar TOTP",
"admin.config.oauth.ignore-totp.description": "Ignorar TOTP cuando el usuario utiliza inicio de sesión social",
"admin.config.oauth.disable-password": "Desactivar el inicio de sesión con contraseña",
"admin.config.oauth.disable-password.description": "Al desactivar el inicio de sesión de contraseña\nAsegúrese de que un proveedor de OAuth está configurado correctamente antes de activar esta configuración para evitar ser bloqueado.",
"admin.config.oauth.github-enabled": "GitHub",
"admin.config.oauth.github-enabled.description": "Si el inicio de sesión de GitHub está habilitado",
"admin.config.oauth.github-client-id": "ID del Cliente de GitHub",
@@ -363,29 +382,60 @@ export default {
"admin.config.oauth.microsoft-enabled": "Microsoft",
"admin.config.oauth.microsoft-enabled.description": "Si el inicio de sesión de Microsoft está habilitado",
"admin.config.oauth.microsoft-tenant": "Microsoft Tenant",
"admin.config.oauth.microsoft-tenant.description": "Tenant ID of the Microsoft OAuth app\ncommon: Users with both a personal Microsoft account and a work or school account from Microsoft Entra ID can sign in to the application. organizations: Only users with work or school accounts from Microsoft Entra ID can sign in to the application.\nconsumers: Only users with a personal Microsoft account can sign in to the application.\ndomain name of the Microsoft Entra tenant or the tenant ID in GUID format: Only users from a specific Microsoft Entra tenant (directory members with a work or school account or directory guests with a personal Microsoft account) can sign in to the application.",
"admin.config.oauth.microsoft-tenant.description": "Tenant ID de la aplicación OAuth de Microsoft\ncomún: Los usuarios con una cuenta personal de Microsoft y una cuenta de trabajo o escuela de Microsoft Entra ID pueden iniciar sesión en la aplicación.\norganizaciones: Solo los usuarios con cuentas de trabajo o escuela de Microsoft Entra ID pueden iniciar sesión en la aplicación.\nconsumidores: Solo los usuarios con una cuenta personal de Microsoft pueden iniciar sesión en la aplicación.\nNombre de dominio del Tenant ID de Microsoft Entra o el Tenant ID en formato GUID: Solo los usuarios de un Tenant específico de Microsoft Entra (miembros del directorio con una cuenta de trabajo o escuela o invitados del directorio con una cuenta personal de Microsoft) pueden iniciar sesión en la aplicación.",
"admin.config.oauth.microsoft-client-id": "ID del cliente de Microsoft",
"admin.config.oauth.microsoft-client-id.description": "ID de cliente de la app OAuth de Microsoft",
"admin.config.oauth.microsoft-client-secret": "Secreto del cliente de Microsoft",
"admin.config.oauth.microsoft-client-secret.description": "Client secret of the Microsoft OAuth app",
"admin.config.oauth.microsoft-client-secret.description": "Secreto del cliente de la aplicación OAuth de Microsoft",
"admin.config.oauth.discord-enabled": "Discord",
"admin.config.oauth.discord-enabled.description": "Si el inicio de sesión de Discord está habilitado",
"admin.config.oauth.discord-limited-guild": "Discord limited server ID",
"admin.config.oauth.discord-limited-users": "Usuarios limitados de Discord",
"admin.config.oauth.discord-limited-users.description": "Limitar el inicio de sesión a usuarios específicos por su ID de Discord. Deja en blanco para desactivar.",
"admin.config.oauth.discord-limited-guild": "ID de servidor limitado de Discord",
"admin.config.oauth.discord-limited-guild.description": "Limitar el inicio de sesión a usuarios en un servidor específico. Déjelo en blanco para desactivarlo.",
"admin.config.oauth.discord-client-id": "Discord Client ID",
"admin.config.oauth.discord-client-id.description": "Client ID of the Discord OAuth app",
"admin.config.oauth.discord-client-secret": "Discord Client secret",
"admin.config.oauth.discord-client-secret.description": "Client secret of the Discord OAuth app",
"admin.config.oauth.discord-client-id": "ID de cliente de Discord",
"admin.config.oauth.discord-client-id.description": "ID de cliente de la aplicación OAuth de Discord",
"admin.config.oauth.discord-client-secret": "Secreto de cliente de Discord",
"admin.config.oauth.discord-client-secret.description": "Secreto del cliente de la aplicación OAuth de Discord",
"admin.config.oauth.oidc-enabled": "Conexión OpenID",
"admin.config.oauth.oidc-enabled.description": "Whether OpenID Connect login is enabled",
"admin.config.oauth.oidc-discovery-uri": "OpenID Connect Discovery URI",
"admin.config.oauth.oidc-discovery-uri.description": "Discovery URI of the OpenID Connect OAuth app",
"admin.config.oauth.oidc-username-claim": "OpenID Connect username claim",
"admin.config.oauth.oidc-username-claim.description": "Username claim in OpenID Connect ID token. Leave it blank if you don't know what this config is.",
"admin.config.oauth.oidc-client-id": "OpenID Connect Client ID",
"admin.config.oauth.oidc-client-id.description": "Client ID of the OpenID Connect OAuth app",
"admin.config.oauth.oidc-client-secret": "OpenID Connect Client secret",
"admin.config.oauth.oidc-client-secret.description": "Client secret of the OpenID Connect OAuth app",
"admin.config.oauth.oidc-enabled.description": "Si el inicio de sesión de OpenID Connect está habilitado",
"admin.config.oauth.oidc-discovery-uri": "URI de descubrimiento de OpenID Connect",
"admin.config.oauth.oidc-discovery-uri.description": "URI de descubrimiento de la aplicación OAuth de OpenID Connect",
"admin.config.oauth.oidc-sign-out": "Cerrar sesión de OpenID Connect",
"admin.config.oauth.oidc-sign-out.description": "Si el botón \"Cerrar sesión\" cerrará la sesión del proveedor de OpenID Connect",
"admin.config.oauth.oidc-scope": "OpenID Connect scope",
"admin.config.oauth.oidc-scope.description": "Scopes which should be requested from the OpenID Connect provider.",
"admin.config.oauth.oidc-username-claim": "Reclamo de nombre de usuario de OpenID Connect",
"admin.config.oauth.oidc-username-claim.description": "Reclamo de nombre de usuario en el token de OpenID Connect. Déjalo en blanco si no sabes qué es esta configuración.",
"admin.config.oauth.oidc-role-path": "Ruta a los roles en el token de OpenID Connect",
"admin.config.oauth.oidc-role-path.description": "Debe ser una ruta JMES válida que haga referencia a un array de roles. " + "Se recomienda gestionar los derechos de acceso utilizando roles de OpenID Connect solo si no se ha configurado ningún otro proveedor de identidad y si el inicio de sesión con contraseña está deshabilitado. " + "Déjalo en blanco si no sabe lo que es esta configuración.",
"admin.config.oauth.oidc-role-general-access": "Rol de OpenID Connect para acceso general",
"admin.config.oauth.oidc-role-general-access.description": "Rol requerido para acceso general. Debe estar presente en los roles de un usuario para que inicie sesión. " + "Déjalo en blanco si no sabe lo que es esta configuración.",
"admin.config.oauth.oidc-role-admin-access": "Rol de OpenID Connect para acceso de administrador",
"admin.config.oauth.oidc-role-admin-access.description": "Rol requerido para el acceso administrativo. Debe estar presente en los roles de un usuario para acceder al panel de administración. " + "Déjalo en blanco si no sabe lo que es esta configuración.",
"admin.config.oauth.oidc-client-id": "ID de Cliente OpenID Connect",
"admin.config.oauth.oidc-client-id.description": "ID de cliente de la aplicación OAuth de OpenID Connect",
"admin.config.oauth.oidc-client-secret": "Secreto de cliente de OpenID Connect",
"admin.config.oauth.oidc-client-secret.description": "Secreto del cliente de la aplicación OAuth de OpenID Connect",
"admin.config.category.ldap": "LDAP",
"admin.config.ldap.enabled": "Habilitar LDAP",
"admin.config.ldap.enabled.description": "Usar autenticación LDAP para el inicio de sesión de usuarios",
"admin.config.ldap.url": "URL del servidor",
"admin.config.ldap.url.description": "URL del servidor LDAP",
"admin.config.ldap.bind-dn": "Bind DN",
"admin.config.ldap.bind-dn.description": "Usuario predeterminado utilizado para realizar la búsqueda de usuarios",
"admin.config.ldap.bind-password": "Bind password",
"admin.config.ldap.bind-password.description": "Contraseña utilizada para realizar la búsqueda de usuarios",
"admin.config.ldap.search-base": "Base de usuarios",
"admin.config.ldap.search-base.description": "Ubicación base, donde se llevará a cabo la búsqueda de usuarios",
"admin.config.ldap.search-query": "Consulta de usuario",
"admin.config.ldap.search-query.description": "La consulta de usuario se utilizará para buscar en la 'Base de usuarios' al usuario LDAP. Se puede usar %username% como marcador de posición para la entrada del usuario.",
"admin.config.ldap.admin-groups": "Grupo de administración",
"admin.config.ldap.admin-groups.description": "Grupo requerido para acceso administrativo.",
"admin.config.ldap.field-name-member-of": "Atributo grupos de usuarios",
"admin.config.ldap.field-name-member-of.description": "Nombre del atributo LDAP para los grupos de los que es miembro un usuario. Esto se utiliza al verificar el grupo de administración.",
"admin.config.ldap.field-name-email": "Atributo correo electrónico del usuario",
"admin.config.ldap.field-name-email.description": "Nombre del atributo LDAP para el correo electrónico de un usuario.",
// 404
"404.description": "Oops esta página no existe.",
"404.button.home": "Regrésame al inicio",
@@ -402,8 +452,8 @@ export default {
"error.msg.already_linked": "Esta cuenta {0} ya está vinculada a otra cuenta.",
"error.msg.not_linked": "Esta cuenta {0} aún no ha sido vinculada a ninguna cuenta.",
"error.msg.unverified_account": "Esta cuenta {0} no está verificada, por favor inténtalo de nuevo después de la verificación.",
"error.msg.discord_guild_permission_denied": "No tienes permitido iniciar sesion.",
"error.msg.cannot_get_user_info": "No se puede obtener la información de usuario de la cuenta {0}.",
"error.msg.user_not_allowed": "No tienes permitido iniciar sesion.",
"error.msg.cannot_get_user_info": "No se puede obtener tu información de usuario de esta cuenta {0}.",
"error.param.provider_github": "GitHub",
"error.param.provider_google": "Google",
"error.param.provider_microsoft": "Microsoft",
@@ -421,8 +471,9 @@ export default {
"common.button.generate": "Generar",
"common.button.done": "Listo",
"common.text.link": "Enlace",
"common.text.navigate-to-link": "Ir al enlace",
"common.text.navigate-to-link": "Visitar enlace",
"common.text.or": "o",
"common.text.redirecting": "Redirigiendo...",
"common.button.go-back": "Volver",
"common.button.go-home": "Página de inicio",
"common.notify.copied": "Tu enlace se ha copiado al portapapeles",
@@ -432,6 +483,8 @@ export default {
"common.error.invalid-email": "Correo electrónico no válido",
"common.error.too-short": "Debe tener al menos {length} caracteres",
"common.error.too-long": "Debe tener como máximo {length} caracteres",
"common.error.number-too-small": "Debe ser al menos {min}",
"common.error.number-too-large": "Debe ser como máximo {max}",
"common.error.exact-length": "Debe tener exactamente {length} caracteres",
"common.error.invalid-number": "Debe ser un número",
"common.error.field-required": "Este campo es requerido"

View File

@@ -3,7 +3,7 @@ export default {
"navbar.upload": "Pilvetä",
"navbar.signin": "Kirjaudu Sisään",
"navbar.home": "Koti",
"navbar.signup": "Rekisteröidy",
"navbar.signup": "Sign up",
"navbar.links.shares": "Minun jakaukset",
"navbar.links.reverse": "Takaperin jaetut",
"navbar.avatar.account": "Oma tIli",
@@ -16,9 +16,9 @@ export default {
"home.bullet.a.name": "Itse-Isännöitty",
"home.bullet.a.description": "Isännöi \"Pingvin Share\" omalla palvelimellasi.",
"home.bullet.b.name": "Yksityisyys",
"home.bullet.b.description": "Sinun tiedostosi ovat sinun ja niiden ei ikinä pidä päättyä kolmannen osapuolen käsiin.",
"home.bullet.b.description": "Your files are yours and will never be accessed by third parties.",
"home.bullet.c.name": "Ei ärsyttävää tiedoston kokorajoitusta",
"home.bullet.c.description": "Lataa niin paljon isoja tiedostoja kuin tykkäät. Vain kovalevysi on rajana.",
"home.bullet.c.description": "Upload files as big as you want. Only your hard drive will be your limit.",
"home.button.start": "Aloita",
"home.button.source": "Lähdekoodi",
// END /
@@ -34,6 +34,7 @@ export default {
"signIn.notify.totp-required.title": "Kaksivaiheinen tunnistautuminen vaadittu",
"signIn.notify.totp-required.description": "Syötä kaksivaiheisen tunnistautumisen koodi tähän",
"signIn.oauth.or": "OR",
"signIn.oauth.signInWith": "Sign in with",
"signIn.oauth.github": "GitHub",
"signIn.oauth.google": "Google",
"signIn.oauth.microsoft": "Microsoft",
@@ -57,12 +58,12 @@ export default {
// /auth/reset-password
"resetPassword.title": "Unohditko salasanan?",
"resetPassword.description": "Kirjoita sähköpostiosoitteesi palauttaaksesi salasanasi.",
"resetPassword.notify.success": "A message with a link to reset your password has been sent if the email exists.",
"resetPassword.notify.success": "A message with a link to reset your password has been sent if the provided email exists.",
"resetPassword.button.back": "Takaisin kirjautumiseen",
"resetPassword.text.resetPassword": "Nollaa salasana",
"resetPassword.text.enterNewPassword": "Anna uusi salasana",
"resetPassword.input.password": "Uusi salasana",
"resetPassword.notify.passwordReset": "Salasanan nollaus onnistui.",
"resetPassword.notify.passwordReset": "Your password has been successfully reset.",
// /account
"account.title": "Oma tIli",
"account.card.info.title": "Tilin tiedot",
@@ -72,7 +73,7 @@ export default {
"account.card.password.title": "Salasana",
"account.card.password.old": "Vanha salasana",
"account.card.password.new": "Uusi salasana",
"account.card.password.noPasswordSet": "You don't have a password set. If you want to sign in with email and password you need to set a password.",
"account.card.password.noPasswordSet": "You do not have a password set. To sign in using your email and password, you need to create a password.",
"account.notify.password.success": "Salasana vaihdettu",
"account.card.oauth.title": "Social login",
"account.card.oauth.github": "GitHub",
@@ -84,7 +85,7 @@ export default {
"account.card.oauth.unlink": "Unlink",
"account.card.oauth.unlinked": "Unlinked",
"account.modal.unlink.title": "Unlink account",
"account.modal.unlink.description": "Unlinking your social accounts may cause you to lose your account if you don't remember your username and password.",
"account.modal.unlink.description": "Unlinking your social accounts may cause you to lose your account if you don't remember your login credentials",
"account.notify.oauth.unlinked.success": "Unlinked successfully",
"account.card.security.title": "Turvallisuus",
"account.card.security.totp.enable.description": "Anna nykyinen salasanasi aloittaaksesi TOTP käytön",
@@ -120,12 +121,12 @@ export default {
"account.shares.table.name": "Nimi",
"account.shares.table.description": "Kuvaus",
"account.shares.table.visitors": "Vierailijat",
"account.shares.table.expiresAt": "Vanhenee",
"account.shares.table.createdAt": "Luotu",
"account.shares.table.expiresAt": "Expires on",
"account.shares.table.createdAt": "Created on",
"account.shares.table.size": "Koko",
"account.shares.modal.share-informations": "Jaetun tiedot",
"account.shares.modal.share-link": "Jaa linkki",
"account.shares.modal.delete.title": "Poista jaettu {share}",
"account.shares.modal.delete.title": "Delete share: {share}",
"account.shares.modal.delete.description": "Haluatko todella poistaa tämän jaetun tiedoston/ot?",
// END /account/shares
// /account/reverseShares
@@ -149,8 +150,12 @@ export default {
"account.reverseShares.modal.expiration.year-singular": "Vuosi",
"account.reverseShares.modal.expiration.year-plural": "Vuotta",
"account.reverseShares.modal.max-size.label": "Suurin tiedostonkoko",
"account.reverseShares.modal.send-email": "Lähetä sähköposti-ilmoitus",
"account.reverseShares.modal.send-email.description": "Lähetä sähköpostiilmoitus kun jako on luotu tällä käänteisellä jakolinkillä.",
"account.reverseShares.modal.send-email": "Send email notifications",
"account.reverseShares.modal.send-email.description": "Sends you an email notification when a share is created with this reverse share link.",
"account.reverseShares.modal.simplified": "Simple mode",
"account.reverseShares.modal.simplified.description": "Make it easy for the person uploading the file to share it with you. They will only be able to customize the name and description of the share.",
"account.reverseShares.modal.public-access": "Public access",
"account.reverseShares.modal.public-access.description": "Make the shares created with this reverse share public. If disabled, only you and the share creator will have access to view it.",
"account.reverseShares.modal.max-use.label": "Käyttökertoja enintään",
"account.reverseShares.modal.max-use.description": "Enimmäismäärä kertoja, joilla tämä URL-osoite voidaan käyttää joita luomiseen.",
"account.reverseShare.never-expires": "Tämä käänteinen jako ei koskaan vanhene.",
@@ -178,14 +183,14 @@ export default {
"admin.users.table.username": "Käyttäjätunnus",
"admin.users.table.email": "Sähköposti",
"admin.users.table.admin": "Ylläpitäjä",
"admin.users.edit.update.title": "Päivitä käyttäjä {username}",
"admin.users.edit.update.title": "Edit user: {username}",
"admin.users.edit.update.admin-privileges": "Ylläpitäjän oikeudet",
"admin.users.edit.update.change-password.title": "Vaihda salasana",
"admin.users.edit.update.change-password.field": "Uusi salasana",
"admin.users.edit.update.change-password.button": "Tallenna uusi salasana",
"admin.users.edit.update.notify.password.success": "Salasana vaihdettu",
"admin.users.edit.delete.title": "Poista käyttäjä {username}",
"admin.users.edit.delete.description": "Haluatko varmasti poistaa tämän käyttäjän ja kaikki hänen jaot?",
"admin.users.edit.delete.title": "Delete user: {username} ?",
"admin.users.edit.delete.description": "Do you really want to delete this user and all their shares?",
// showCreateUserModal.tsx
"admin.users.modal.create.title": "Luo käyttäjä",
"admin.users.modal.create.username": "Käyttäjätunnus",
@@ -201,17 +206,20 @@ export default {
"admin.shares.table.id": "Share ID",
"admin.shares.table.username": "Creator",
"admin.shares.table.visitors": "Visitors",
"admin.shares.table.expires": "Expires At",
"admin.shares.edit.delete.title": "Delete share {id}",
"admin.shares.table.expires": "Expires on",
"admin.shares.edit.delete.title": "Delete share: {id}",
"admin.shares.edit.delete.description": "Do you really want to delete this share?",
// END /admin/shares
// /upload
"upload.title": "Pilvetä",
"upload.notify.confirm-leave": "Are you sure you want to leave this page? Your upload will be canceled.",
"upload.notify.generic-error": "Kohdattiin odottamaton virhe jaon luomisessa.",
"upload.notify.count-failed": "{count} tiedostoa ei voitu ladata. Yritetään uudelleen.",
"upload.reverse-share.error.invalid.title": "Invalid reverse share link",
"upload.reverse-share.error.invalid.description": "This reverse share has expired or is invalid.",
// Dropzone.tsx
"upload.dropzone.title": "Pilvetä tiedostoja",
"upload.dropzone.description": "Vedä ja pudota tiedostot tähän aloittaaksesi jakamisen. Voimme hyväksyä vain tiedostot, jotka ovat yhteensä alle {maxSize}.",
"upload.dropzone.description": "Drag'n'drop files here to start your share. We only accept files up to {maxSize} in total.",
"upload.dropzone.notify.file-too-big": "Tiedostojen enimmäiskoko ylittää {maxSize} -arvon enimmäismäärän.",
// FileList.tsx
"upload.filelist.name": "Nimi",
@@ -223,8 +231,8 @@ export default {
"upload.modal.not-signed-in": "Et ole kirjautunut sisään",
"upload.modal.not-signed-in-description": "Et voi poistaa jakoasi manuaalisesti ja tarkastella kävijöiden määrää.",
"upload.modal.expires.never": "ei koskaan",
"upload.modal.expires.never-long": "Ei vanhene koskaan",
"upload.modal.expires.error.too-long": "Expiration exceeds maximum expiration date of {max}.",
"upload.modal.expires.never-long": "Permanent share",
"upload.modal.expires.error.too-long": "Expiration date exceeds the maximum of {max}.",
"upload.modal.link.label": "Linkki",
"upload.modal.expires.label": "Vanhentuminen",
"upload.modal.expires.minute-singular": "Minuutti",
@@ -254,6 +262,7 @@ export default {
"upload.modal.completed.never-expires": "Tämä käänteinen jako ei koskaan vanhene.",
"upload.modal.completed.expires-on": "Tämä käänteinen jako vanhenee kun on {expiration}.",
"upload.modal.completed.share-ready": "Jako valmiina",
"upload.modal.completed.notified-reverse-share-creator": "We have notified the creator of the reverse share. You can also manually share this link with them through other means.",
// END /upload
// /share/[id]
"share.title": "Jaa {shareId}",
@@ -263,17 +272,19 @@ export default {
"share.error.removed.title": "Jako poistettu",
"share.error.not-found.title": "Jakoa ei löydetty",
"share.error.not-found.description": "Etsimääsi sivua ei ole olemassa.",
"share.error.access-denied.title": "Private share",
"share.error.access-denied.description": "The current account does not have permission to access this share",
"share.modal.password.title": "Salasana vaaditaan",
"share.modal.password.description": "Päästäksesi käsiksi tähän jakoon anna jaon salasana.",
"share.modal.password.description": "Please enter the password to acces this share.",
"share.modal.password": "Salasana",
"share.modal.error.invalid-password": "Virheellinen salasana",
"share.button.download-all": "Lataa kaikki",
"share.notify.download-all-preparing": "Jako on valmistumassa. Yritä uudelleen muutaman minuutin kuluttua.",
"share.notify.download-all-preparing": "The share is being prepared. Please try again in a few minutes.",
"share.modal.file-link": "Tiedoston linkki",
"share.table.name": "Nimi",
"share.table.size": "Koko",
"share.modal.file-preview.error.not-supported.title": "Esikatselua ei tuettu",
"share.modal.file-preview.error.not-supported.description": "A preview for this file type is unsupported. Please download the file to view it.",
"share.modal.file-preview.error.not-supported.description": "Previews are not supported for this type of files. Please download the file to view it.",
// END /share/[id]
// /share/[id]/edit
"share.edit.title": "Edit {shareId}",
@@ -292,6 +303,8 @@ export default {
"admin.config.general.app-name.description": "Sovelluksen nimi",
"admin.config.general.app-url": "Sovelluksen URL",
"admin.config.general.app-url.description": "Millä URL-osoitteella Pingvin Share on saatavilla",
"admin.config.general.secure-cookies": "Secure cookies",
"admin.config.general.secure-cookies.description": "Whether to set the secure flag on cookies. If enabled, the site will not function when accessed over HTTP.",
"admin.config.general.show-home-page": "Näytä kotisivu",
"admin.config.general.show-home-page.description": "Näytetäänkö kotisivu vai ei",
"admin.config.general.session-duration": "Session Duration",
@@ -299,55 +312,61 @@ export default {
"admin.config.general.logo": "Logo",
"admin.config.general.logo.description": "Muuta logoa lataamalla uusi kuva. Kuvan on oltava PNG ja sen on oltava formaatti 1:1.",
"admin.config.general.logo.placeholder": "Valitse kuva",
"admin.config.email.enable-share-email-recipients": "Salli sähköpostin vastaanottajien jakaminen",
"admin.config.email.enable-share-email-recipients.description": "Salli sähköpostien jakaminen vastaanottajille. Ota tämä käyttöön vain, jos olet ottanut SMTP:n käyttöön.",
"admin.config.email.enable-share-email-recipients": "Enable email recipient sharing",
"admin.config.email.enable-share-email-recipients.description": "Whether to allow email sharing with recipients. Only enable this if SMTP is activated.",
"admin.config.email.share-recipients-subject": "Sähköpostijaon otsikko",
"admin.config.email.share-recipients-subject.description": "Sähköpostin aihe, joka saa lähetetään jaon vastaanottajille.",
"admin.config.email.share-recipients-message": "Sähköpostijaon viesti",
"admin.config.email.share-recipients-message.description": "Viesti joka lähetetään jaon vastaanottajille. Saatavilla olevat muuttujat:\n {creator} - Jaon luojan käyttäjänimi\n {shareUrl} - Jaon URL\n {desc} - Jaon kuvaus\n {expires} - Jaon päättymispäivä\n Muuttujat korvataan todellisella arvolla.",
"admin.config.email.share-recipients-message.description": "Message which gets sent to the share recipients. Available variables:\n {creator} - The username of the creator of the share\n {creatorEmail} - The email of the creator of the share\n {shareUrl} - The URL of the share\n {desc} - The description of the share\n {expires} - The expiration date of the share\n These variables will be replaced with the actual value.",
"admin.config.email.reverse-share-subject": "Käänteisen jaon aihe",
"admin.config.email.reverse-share-subject.description": "Aihe sähköpostin joka lähetetään kun joku loi jaon käänteisen jakolinkin kanssa.",
"admin.config.email.reverse-share-subject.description": "Subject of the sent email when someone created a share with your reverse share link.",
"admin.config.email.reverse-share-message": "Käänteisen jakoviestin viesti",
"admin.config.email.reverse-share-message.description": "Viesti joka lähetetään kun joku loi jaon käänteisen jakolinkin kanssa. {shareUrl} korvataan luojan nimellä ja jaon URL:lla.",
"admin.config.email.reset-password-subject": "Nollaa salasanan aihe",
"admin.config.email.reset-password-subject.description": "Sähköpostin aihe, joka lähetetään kun käyttäjä pyytää salasanan palauttamista.",
"admin.config.email.reset-password-subject.description": "Subject of the sent email when a user requests a password reset.",
"admin.config.email.reset-password-message": "Nollaa salasanan viesti",
"admin.config.email.reset-password-message.description": "Viesti joka lähetetään kun käyttäjä pyytää salasanan nollausta. {url} korvataan nollaussalasanan URL-osoitteella.",
"admin.config.email.invite-subject": "Kutsun aihe",
"admin.config.email.invite-subject.description": "Sähköpostin aihe, mikä lähetetään kun ylläpitäjä kutsuu käyttäjää.",
"admin.config.email.invite-subject.description": "Subject of the sent email when an admin invites a user.",
"admin.config.email.invite-message": "Kutsun viesti",
"admin.config.email.invite-message.description": "Viesti mikä lähetetään kuin yp invaa käyttäjän. {url} korvataan kutsuosoitteella ja {password} salasanalla.",
"admin.config.email.invite-message.description": "Message which gets sent when an admin invites a user. {url} will be replaced with the invite URL, {email} with the email and {password} with the users password.",
"admin.config.share.allow-registration": "Salli rekisteröinti",
"admin.config.share.allow-registration.description": "Onko rekisteröinti sallittu",
"admin.config.share.allow-unauthenticated-shares": "Salli anonyymit jaot",
"admin.config.share.allow-unauthenticated-shares.description": "Voiko tunnistamattomat käyttäjät luoda jakoja",
"admin.config.share.max-expiration": "Max expiration",
"admin.config.share.max-expiration.description": "Maximum share expiration in hours. Set to 0 to allow unlimited expiration.",
"admin.config.share.share-id-length": "Default share ID length",
"admin.config.share.share-id-length.description": "Default length for the generated ID of a share. This value is also used to generate links for reverse shares. A value below 8 is not considered secure.",
"admin.config.share.max-size": "Maksimikoko",
"admin.config.share.max-size.description": "Jaon enimmäiskoko tavuissa (bytes)",
"admin.config.share.zip-compression-level": "Zip puristustaso",
"admin.config.share.zip-compression-level.description": "Säädä tasoa tiedoston koon ja pakkausnopeuden välillä. Kelvolliset arvot vaihtelevat 09, 0 ei puristusta ja 9 on suurin puristusvoima. ",
"admin.config.share.chunk-size": "Chunk size",
"admin.config.share.chunk-size.description": "Adjust the chunk size (in bytes) for your uploads to balance efficiency and reliability according to your internet connection. Smaller chunks can enhance success rates for unstable connections, while larger chunks speed up uploads for stable connections.",
"admin.config.share.chunk-size.description": "Adjust the chunk size (in bytes) for your uploads to balance efficiency and reliability according to your internet connection. Smaller chunks can enhance success rates for unstable connections, while larger chunks make uploads faster for stable connections.",
"admin.config.share.auto-open-share-modal": "Auto open create share modal",
"admin.config.share.auto-open-share-modal.description": "The share creation modal automatically appears when a user selects files, eliminating the need to manually click the button.",
"admin.config.smtp.enabled": "Käytössä",
"admin.config.smtp.enabled": "Enable",
"admin.config.smtp.enabled.description": "Onko SMTP käytössä. Aseta tämä todeksi vain, jos olet syöttänyt SMTP-palvelimen isäntän, portin, sähköpostin, käyttäjän ja salasanan.",
"admin.config.smtp.host": "Isäntä",
"admin.config.smtp.host.description": "SMTP palvelimen isäntä",
"admin.config.smtp.port": "Portti",
"admin.config.smtp.port.description": "SMTP palvelimen portti",
"admin.config.smtp.email": "Sähköposti",
"admin.config.smtp.email.description": "Sähköpostiosoite, josta sähköpostit on lähetetty",
"admin.config.smtp.email.description": "Email address from wich the emails get sent",
"admin.config.smtp.username": "Käyttäjätunnus",
"admin.config.smtp.username.description": "SMTP palvelimen käyttäjänimi",
"admin.config.smtp.password": "Salasana",
"admin.config.smtp.password.description": "SMTP palvelimen salasana",
"admin.config.smtp.button.test": "Lähetä testisähköposti",
"admin.config.smtp.allow-unauthorized-certificates": "Trust unauthorized SMTP server certificates",
"admin.config.smtp.allow-unauthorized-certificates.description": "Only set this to true if you need to trust self signed certificates.",
"admin.config.oauth.allow-registration": "Allow registration",
"admin.config.oauth.allow-registration.description": "Allow users to register via social login",
"admin.config.oauth.ignore-totp": "Ignore TOTP",
"admin.config.oauth.ignore-totp.description": "Whether to ignore TOTP when user using social login",
"admin.config.oauth.disable-password": "Disable password login",
"admin.config.oauth.disable-password.description": "Whether to disable password login\nMake sure that an OAuth provider is properly configured before activating this configuration to avoid being locked out.",
"admin.config.oauth.github-enabled": "GitHub",
"admin.config.oauth.github-enabled.description": "Whether GitHub login is enabled",
"admin.config.oauth.github-client-id": "GitHub Client ID",
@@ -370,6 +389,8 @@ export default {
"admin.config.oauth.microsoft-client-secret.description": "Client secret of the Microsoft OAuth app",
"admin.config.oauth.discord-enabled": "Discord",
"admin.config.oauth.discord-enabled.description": "Whether Discord login is enabled",
"admin.config.oauth.discord-limited-users": "Discord limited users",
"admin.config.oauth.discord-limited-users.description": "Limit signing in to specific users by their Discord ID. Leave it blank to disable.",
"admin.config.oauth.discord-limited-guild": "Discord limited server ID",
"admin.config.oauth.discord-limited-guild.description": "Limit signing in to users in a specific server. Leave it blank to disable.",
"admin.config.oauth.discord-client-id": "Discord Client ID",
@@ -380,12 +401,41 @@ export default {
"admin.config.oauth.oidc-enabled.description": "Whether OpenID Connect login is enabled",
"admin.config.oauth.oidc-discovery-uri": "OpenID Connect Discovery URI",
"admin.config.oauth.oidc-discovery-uri.description": "Discovery URI of the OpenID Connect OAuth app",
"admin.config.oauth.oidc-sign-out": "Sign out from OpenID Connect",
"admin.config.oauth.oidc-sign-out.description": "Whether the “Sign out” button will sign out from the OpenID Connect provider",
"admin.config.oauth.oidc-scope": "OpenID Connect scope",
"admin.config.oauth.oidc-scope.description": "Scopes which should be requested from the OpenID Connect provider.",
"admin.config.oauth.oidc-username-claim": "OpenID Connect username claim",
"admin.config.oauth.oidc-username-claim.description": "Username claim in OpenID Connect ID token. Leave it blank if you don't know what this config is.",
"admin.config.oauth.oidc-role-path": "Path to roles in OpenID Connect token",
"admin.config.oauth.oidc-role-path.description": "Must be a valid JMES path referencing an array of roles. " + "Managing access rights using OpenID Connect roles is only recommended if no other identity provider is configured and password login is disabled. " + "Leave it blank if you don't know what this config is.",
"admin.config.oauth.oidc-role-general-access": "OpenID Connect role for general access",
"admin.config.oauth.oidc-role-general-access.description": "Role required for general access. Must be present in a users roles for them to log in. " + "Leave it blank if you don't know what this config is.",
"admin.config.oauth.oidc-role-admin-access": "OpenID Connect role for admin access",
"admin.config.oauth.oidc-role-admin-access.description": "Role required for administrative access. Must be present in a users roles for them to access the admin panel. " + "Leave it blank if you don't know what this config is.",
"admin.config.oauth.oidc-client-id": "OpenID Connect Client ID",
"admin.config.oauth.oidc-client-id.description": "Client ID of the OpenID Connect OAuth app",
"admin.config.oauth.oidc-client-secret": "OpenID Connect Client secret",
"admin.config.oauth.oidc-client-secret.description": "Client secret of the OpenID Connect OAuth app",
"admin.config.category.ldap": "LDAP",
"admin.config.ldap.enabled": "Enable LDAP",
"admin.config.ldap.enabled.description": "Use LDAP authentication for user login",
"admin.config.ldap.url": "Server URL",
"admin.config.ldap.url.description": "URL of the LDAP server",
"admin.config.ldap.bind-dn": "Bind DN",
"admin.config.ldap.bind-dn.description": "Default user used to perform the user search",
"admin.config.ldap.bind-password": "Bind password",
"admin.config.ldap.bind-password.description": "Password used to perform the user search",
"admin.config.ldap.search-base": "User base",
"admin.config.ldap.search-base.description": "Base location, where the user search will be performed",
"admin.config.ldap.search-query": "User query",
"admin.config.ldap.search-query.description": "The user query will be used to search the 'User base' for the LDAP user. %username% can be used as the placeholder for the user given input.",
"admin.config.ldap.admin-groups": "Admin group",
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
"admin.config.ldap.field-name-member-of": "User groups attribute name",
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
"admin.config.ldap.field-name-email": "User email attribute name",
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
// 404
"404.description": "Hups tätä sivua ei ole olemassa.",
"404.button.home": "Tuo minut takaisin kotiin",
@@ -400,10 +450,10 @@ export default {
"error.msg.no_user": "User linked to this {0} account doesn't exist.",
"error.msg.no_email": "Can't get email address from this {0} account.",
"error.msg.already_linked": "This {0} account is already linked to another account.",
"error.msg.not_linked": "This {0} account haven't linked to any account yet.",
"error.msg.not_linked": "This {0} account hasn't been linked to any account yet.",
"error.msg.unverified_account": "This {0} account is unverified, please try again after verification.",
"error.msg.discord_guild_permission_denied": "You are not allowed to sign in.",
"error.msg.cannot_get_user_info": "Can not get your user info from this {0} account.",
"error.msg.user_not_allowed": "You are not allowed to sign in.",
"error.msg.cannot_get_user_info": "Cannot get your user info from this {0} account.",
"error.param.provider_github": "GitHub",
"error.param.provider_google": "Google",
"error.param.provider_microsoft": "Microsoft",
@@ -421,8 +471,9 @@ export default {
"common.button.generate": "Luo",
"common.button.done": "Valmis",
"common.text.link": "Linkki",
"common.text.navigate-to-link": "Go to the link",
"common.text.navigate-to-link": "Visit link",
"common.text.or": "tai",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "Takaisin",
"common.button.go-home": "Go home",
"common.notify.copied": "Linkki kopioitiin leikepöydälle",
@@ -432,6 +483,8 @@ export default {
"common.error.invalid-email": "Virheellinen sähköpostiosoite",
"common.error.too-short": "Täytyy olla vähintään {length} merkkiä",
"common.error.too-long": "Täytyy olla enintään {length} merkkiä",
"common.error.number-too-small": "Must be at least {min}",
"common.error.number-too-large": "Must be at most {max}",
"common.error.exact-length": "On oltava tarkasti {length} merkkiä pitkä",
"common.error.invalid-number": "Pitää olla luku",
"common.error.field-required": "Tämä kenttä on pakollinen"

View File

@@ -11,29 +11,30 @@ export default {
"navbar.avatar.signout": "Se déconnecter",
// END navbar
// /
"home.title": "Une plateforme de partage de fichier <h>auto-hébergée</h>.",
"home.title": "Une plateforme de partage de fichiers <h>autohébergée</h>.",
"home.description": "Voulez-vous vraiment remettre vos fichiers personnels dans les mains de tiers comme WeTransfer ?",
"home.bullet.a.name": "Auto-hébergé",
"home.bullet.a.description": "Hébergez Pingvin Share sur votre propre machine.",
"home.bullet.b.name": "Confidentialité",
"home.bullet.b.description": "Vos fichiers sont vos fichiers et ne devraient jamais être mis entre les mains de tiers.",
"home.bullet.b.description": "Vos fichiers sont les vôtres et ne seront jamais consultés par des tiers.",
"home.bullet.c.name": "Aucune rébarbative limite de taille",
"home.bullet.c.description": "Téléchargez des fichiers aussi volumineux que vous le souhaitez. Seul votre disque dur est la limite.",
"home.bullet.c.description": "Téléchargez des fichiers aussi volumineux que vous le souhaitez. Votre disque dur est votre limite.",
"home.button.start": "Commencer",
"home.button.source": "Code source",
// END /
// /auth/signin
"signin.title": "Content de vous revoir",
"signin.description": "Pas encore de compte ?",
"signin.description": "Vous n'avez pas encore de compte ?",
"signin.button.signup": "Sinscrire",
"signin.input.email-or-username": "Courriel ou surnom",
"signin.input.email-or-username.placeholder": "Votre courriel ou surnom",
"signin.input.email-or-username": "Courriel ou nom d'utilisateur",
"signin.input.email-or-username.placeholder": "Votre courriel ou nom d'utilisateur",
"signin.input.password": "Mot de passe",
"signin.input.password.placeholder": "Votre mot de passe",
"signin.button.submit": "Se connecter",
"signIn.notify.totp-required.title": "Une authentification à deux facteurs est requise",
"signIn.notify.totp-required.description": "Veuillez entrer votre code d'authentification à deux facteurs",
"signIn.oauth.or": "OU",
"signIn.oauth.signInWith": "Se connecter avec",
"signIn.oauth.github": "GitHub",
"signIn.oauth.google": "Google",
"signIn.oauth.microsoft": "Microsoft",
@@ -44,8 +45,8 @@ export default {
"signup.title": "Créer un compte",
"signup.description": "Vous avez déjà un compte ?",
"signup.button.signin": "Se connecter",
"signup.input.username": "Surnom",
"signup.input.username.placeholder": "Votre surnom",
"signup.input.username": "Nom d'utilisateur",
"signup.input.username.placeholder": "Votre nom d'utilisateur",
"signup.input.email": "Adresse email",
"signup.input.email.placeholder": "Votre courriel",
"signup.button.submit": "Commençons",
@@ -57,12 +58,12 @@ export default {
// /auth/reset-password
"resetPassword.title": "Mot de passe oublié ?",
"resetPassword.description": "Saisissez votre courriel pour réinitialiser votre mot de passe.",
"resetPassword.notify.success": "Un message avec un lien pour réinitialiser votre mot de passe a été envoyé si le courriel existe.",
"resetPassword.notify.success": "Un message avec un lien pour réinitialiser votre mot de passe a été envoyé si le courriel fourni existe.",
"resetPassword.button.back": "Retour à la page de connexion",
"resetPassword.text.resetPassword": "Réinitialiser le mot de passe",
"resetPassword.text.enterNewPassword": "Saisissez votre nouveau mot de passe",
"resetPassword.input.password": "Nouveau mot de passe",
"resetPassword.notify.passwordReset": "Votre mot de passe a bien été réinitialisé.",
"resetPassword.notify.passwordReset": "Votre mot de passe a été réinitialisé avec succès.",
// /account
"account.title": "Mon compte",
"account.card.info.title": "Détails du compte",
@@ -72,7 +73,7 @@ export default {
"account.card.password.title": "Mot de passe",
"account.card.password.old": "Ancien mot de passe",
"account.card.password.new": "Nouveau mot de passe",
"account.card.password.noPasswordSet": "Vous navez pas de mot de passe défini. Si vous voulez vous connecter avec un e-mail et un mot de passe, vous devez définir un mot de passe.",
"account.card.password.noPasswordSet": "Vous navez pas de mot de passe défini. Si vous voulez vous connecter avec un courriel et un mot de passe, vous devez définir un mot de passe.",
"account.notify.password.success": "Le mot de passe a été modifié avec succès",
"account.card.oauth.title": "Identifiant social",
"account.card.oauth.github": "GitHub",
@@ -84,20 +85,20 @@ export default {
"account.card.oauth.unlink": "Dissocier",
"account.card.oauth.unlinked": "Dissocié",
"account.modal.unlink.title": "Dissocier le compte",
"account.modal.unlink.description": "Dissocier vos comptes de réseaux sociaux peut vous faire perdre votre compte si jamais vous ne vous souvenez pas de votre nom dutilisateur et de votre mot de passe.",
"account.modal.unlink.description": "Délier vos réseaux sociaux peut vous faire perdre votre compte si vous ne vous souvenez pas de vos identifiants de connexion",
"account.notify.oauth.unlinked.success": "Dissocié avec succès",
"account.card.security.title": "Sécurité",
"account.card.security.totp.enable.description": "Entrez votre mot de passe actuel pour activer TOTP",
"account.card.security.totp.disable.description": "Entrez votre mot de passe actuel pour désactiver TOTP",
"account.card.security.totp.button.start": "Démarrer",
"account.modal.totp.title": "Activer TOTP",
"account.modal.totp.step1": "Étape 1 : Ajouter votre authentificateur",
"account.modal.totp.title": "Activer la double authentification",
"account.modal.totp.step1": "Étape 1 : Ajouter votre authentification",
"account.modal.totp.step2": "Étape 2 : Valider votre code",
"account.modal.totp.enterManually": "Saisir manuellement",
"account.modal.totp.code": "Code",
"common.button.clickToCopy": "Cliquez pour copier",
"account.modal.totp.verify": "Vérifier",
"account.notify.totp.disable": "TOTP désactivé avec succès",
"account.notify.totp.disable": "Double authentification (TOTP) désactivé avec succès",
"account.notify.totp.enable": "TOTP activé avec succès",
"account.card.language.title": "Langue",
"account.card.language.description": "Le projet est traduit par la communauté. Certaines traductions peuvent être incomplètes.",
@@ -125,7 +126,7 @@ export default {
"account.shares.table.size": "Taille",
"account.shares.modal.share-informations": "Détails du partage",
"account.shares.modal.share-link": "Lien de partage",
"account.shares.modal.delete.title": "Supprimer le partage {share}",
"account.shares.modal.delete.title": "Supprimer le partage : {share}",
"account.shares.modal.delete.description": "Voulez-vous vraiment supprimer ce partage ?",
// END /account/shares
// /account/reverseShares
@@ -149,9 +150,13 @@ export default {
"account.reverseShares.modal.expiration.year-singular": "An",
"account.reverseShares.modal.expiration.year-plural": "Ans",
"account.reverseShares.modal.max-size.label": "Taille maximale du partage",
"account.reverseShares.modal.send-email": "Envoyer un courriel de notification",
"account.reverseShares.modal.send-email.description": "Envoyer une notification par courriel lorsqu'un partage est créé depuis ce partage inversé.",
"account.reverseShares.modal.max-use.label": "Nombre d'utilisation max",
"account.reverseShares.modal.send-email": "Envoyer des notifications par courriel",
"account.reverseShares.modal.send-email.description": "Vous envoi une notification par courriel lorsqu'un partage est créé avec ce lien de partage inversé.",
"account.reverseShares.modal.simplified": "Mode simple",
"account.reverseShares.modal.simplified.description": "Simplifiez la tâche à la personne qui vous partage un fichier. Ils ne pourront que personnaliser le nom et la description du partage.",
"account.reverseShares.modal.public-access": "Accès public",
"account.reverseShares.modal.public-access.description": "Rendre les partages créés avec ce partage inversé public. Si désactivé, seul vous et le créateur du partage pourrez le voir.",
"account.reverseShares.modal.max-use.label": "Nombre d'utilisations max",
"account.reverseShares.modal.max-use.description": "Le nombre maximal de fois que cette URL peut être utilisée pour créer un partage.",
"account.reverseShare.never-expires": "Ce partage inversé n'expirera jamais.",
"account.reverseShare.expires-on": "Ce partage inversé expirera le {expiration}.",
@@ -178,14 +183,14 @@ export default {
"admin.users.table.username": "Surnom",
"admin.users.table.email": "Courriel",
"admin.users.table.admin": "Admin",
"admin.users.edit.update.title": "Modifier l'utilisateur {username}",
"admin.users.edit.update.title": "Modifier l'utilisateur : {username}",
"admin.users.edit.update.admin-privileges": "Privilèges dadmin",
"admin.users.edit.update.change-password.title": "Changer le mot de passe",
"admin.users.edit.update.change-password.field": "Nouveau mot de passe",
"admin.users.edit.update.change-password.button": "Enregistrer le nouveau mot de passe",
"admin.users.edit.update.notify.password.success": "Le mot de passe a été modifié",
"admin.users.edit.delete.title": "Supprimer l'utilisateur {username}",
"admin.users.edit.delete.description": "Voulez-vous vraiment supprimer cet utilisateur et toutes ses partages ?",
"admin.users.edit.delete.title": "Supprimer l'utilisateur : {username} ?",
"admin.users.edit.delete.description": "Souhaitez-vous vraiment supprimer cet utilisateur et tous ses partages ?",
// showCreateUserModal.tsx
"admin.users.modal.create.title": "Créer un utilisateur",
"admin.users.modal.create.username": "Surnom",
@@ -202,16 +207,19 @@ export default {
"admin.shares.table.username": "Créateur",
"admin.shares.table.visitors": "Visiteurs",
"admin.shares.table.expires": "Expire le",
"admin.shares.edit.delete.title": "Supprimer le partage {id}",
"admin.shares.edit.delete.title": "Supprimer le partage : {id}",
"admin.shares.edit.delete.description": "Voulez-vous vraiment supprimer ce partage ?",
// END /admin/shares
// /upload
"upload.title": "Envoyer",
"upload.notify.confirm-leave": "Êtes-vous sûr de vouloir quitter cette page ? Votre envoi sera annulé.",
"upload.notify.generic-error": "Une erreur est survenue durant le traitement de votre partage.",
"upload.notify.count-failed": "{count} fichier(s) na(ont) pas pu être envoyé(s). Veuillez réessayer.",
"upload.reverse-share.error.invalid.title": "Lien de partage inversé invalide",
"upload.reverse-share.error.invalid.description": "Ce partage inversé a expiré ou n'est pas valide.",
// Dropzone.tsx
"upload.dropzone.title": "Téléverser des fichiers",
"upload.dropzone.description": "Glissez-déposez des fichiers ici pour commencer votre partage. Ils ne peuvent avoir une taille supérieure à {maxSize} au total.",
"upload.dropzone.description": "Glisser-déposer les fichiers ici pour commencer votre partage. Nous n'acceptons que les fichiers jusqu'à {maxSize} au total.",
"upload.dropzone.notify.file-too-big": "Vos fichiers dépassent la taille maximale de {maxSize}.",
// FileList.tsx
"upload.filelist.name": "Nom",
@@ -223,8 +231,8 @@ export default {
"upload.modal.not-signed-in": "Vous n'êtes pas connecté",
"upload.modal.not-signed-in-description": "Vous ne pourrez pas supprimer votre partage manuellement et afficher le nombre de visiteurs.",
"upload.modal.expires.never": "jamais",
"upload.modal.expires.never-long": "Nexpire jamais",
"upload.modal.expires.error.too-long": "Lexpiration dépasse la date d'expiration du {max}.",
"upload.modal.expires.never-long": "Partage permanent",
"upload.modal.expires.error.too-long": "La date d'expiration dépasse le maximum de {max}.",
"upload.modal.link.label": "Lien",
"upload.modal.expires.label": "Expiration",
"upload.modal.expires.minute-singular": "Minute",
@@ -254,6 +262,7 @@ export default {
"upload.modal.completed.never-expires": "Ce partage nexpirera jamais.",
"upload.modal.completed.expires-on": "Ce partage expirera le {expiration}.",
"upload.modal.completed.share-ready": "Partage prêt",
"upload.modal.completed.notified-reverse-share-creator": "Nous avons notifié le créateur du partage inverse. Vous pouvez également partager manuellement ce lien avec eux par d'autres moyens.",
// END /upload
// /share/[id]
"share.title": "Partage {shareId}",
@@ -263,8 +272,10 @@ export default {
"share.error.removed.title": "Partage supprimé",
"share.error.not-found.title": "Partage introuvable",
"share.error.not-found.description": "Le partage que vous cherchez nexiste pas.",
"share.error.access-denied.title": "Partage privé",
"share.error.access-denied.description": "Le compte actuel n'a pas la permission d'accéder à ce partage",
"share.modal.password.title": "Mot de passe requis",
"share.modal.password.description": "Pour accéder à ce partage, veuillez entrer le mot de passe du partage.",
"share.modal.password.description": "Veuillez entrer le mot de passe d'accès à ce partage.",
"share.modal.password": "Mot de passe",
"share.modal.error.invalid-password": "Mot de passe incorrect",
"share.button.download-all": "Télécharger tout",
@@ -273,7 +284,7 @@ export default {
"share.table.name": "Nom",
"share.table.size": "Taille",
"share.modal.file-preview.error.not-supported.title": "Aperçu non supporté",
"share.modal.file-preview.error.not-supported.description": "Un aperçu de ce type de fichier n'est pas pris en charge. Veuillez télécharger le fichier pour le visualiser.",
"share.modal.file-preview.error.not-supported.description": "Les aperçus ne sont pas pris en charge pour ce type de fichiers. Veuillez télécharger le fichier pour le voir.",
// END /share/[id]
// /share/[id]/edit
"share.edit.title": "Modifier {shareId}",
@@ -292,19 +303,21 @@ export default {
"admin.config.general.app-name.description": "Le nom de lapplication",
"admin.config.general.app-url": "URL de lappli",
"admin.config.general.app-url.description": "Depuis quel URL le partage Pingvin est disponible",
"admin.config.general.secure-cookies": "Cookies sécurisés",
"admin.config.general.secure-cookies.description": "Définir ou non le marquage sécurisé sur les cookies. Si activé, le site ne fonctionnera pas lorsqu'il est accédé via HTTP.",
"admin.config.general.show-home-page": "Afficher la page daccueil",
"admin.config.general.show-home-page.description": "Afficher ou non la page daccueil",
"admin.config.general.session-duration": "Session Duration",
"admin.config.general.session-duration.description": "Time in hours after which a user must log in again (default: 3 months).",
"admin.config.general.session-duration": "Durée de la session",
"admin.config.general.session-duration.description": "Nombre dheures après lesquelles un utilisateur doit se reconnecter (par défaut : 3 mois).",
"admin.config.general.logo": "Logo",
"admin.config.general.logo.description": "Changez de logo en envoyant une nouvelle image. Limage doit être au format PNG et doit avoir un ratio 1:1.",
"admin.config.general.logo.placeholder": "Sélectionner une image",
"admin.config.email.enable-share-email-recipients": "Autoriser le partage par courriel",
"admin.config.email.enable-share-email-recipients.description": "Permet d'envoyer le lien du partage par courriel. N'activez cette option que si vous avez activé SMTP.",
"admin.config.email.enable-share-email-recipients": "Activer le partage par courriel",
"admin.config.email.enable-share-email-recipients.description": "Autoriser ou non le partage de courriels avec les destinataires. N'activer cette option que si SMTP est activé.",
"admin.config.email.share-recipients-subject": "Sujet dun partage",
"admin.config.email.share-recipients-subject.description": "Intitulé du courriel envoyé aux destinataires dun partage.",
"admin.config.email.share-recipients-message": "Message dun partage",
"admin.config.email.share-recipients-message.description": "Contenu du courriel qui est envoyé aux destinataires du partage. Variables possibles :\n {creator} : Le pseudo de lauteur du partage\n {shareUrl} : LURL du partage\n {desc} : La description du partage\n {expires} : La date d'expiration du partage\nLes variables seront remplacées par leur valeur réelle.",
"admin.config.email.share-recipients-message.description": "Contenu du courriel qui est envoyé aux destinataires du partage. Variables possibles :\n {creator} - Le nom d'utilisateur du créateur du partage\n {creatorEmail} - L'adresse mail du créateur du partage\n {shareUrl} - L'URL du partage\n {desc} - La description du partage\n {expires} - La date d'expiration du partage.\n Ces variables seront remplacées par leur valeur effective.",
"admin.config.email.reverse-share-subject": "Sujet dun partage inversé",
"admin.config.email.reverse-share-subject.description": "Intitulé du courriel envoyé lorsque quelquun a partagé des fichiers depuis votre partage inversé.",
"admin.config.email.reverse-share-message": "Message dun partage inversé",
@@ -316,13 +329,15 @@ export default {
"admin.config.email.invite-subject": "Sujet dune invitation",
"admin.config.email.invite-subject.description": "Intitulé du courriel envoyé lorsquun administrateur invite un utilisateur.",
"admin.config.email.invite-message": "Message dune invitation",
"admin.config.email.invite-message.description": "Contenu du courriel envoyé lorsquun administrateur invite un utilisateur. {url} sera remplacé par lURL d'invitation et {password} par le mot de passe.",
"admin.config.email.invite-message.description": "Message qui est envoyé lorsqu'un administrateur invite un utilisateur. {url} sera remplacé avec l'URL d'invitation, {email} avec le courriel et {password} avec le mot de passe de l'utilisateur.",
"admin.config.share.allow-registration": "Autoriser les inscriptions",
"admin.config.share.allow-registration.description": "Permet aux visiteurs de créer un compte.",
"admin.config.share.allow-registration.description": "Permet aux visiteurs de créer un compte",
"admin.config.share.allow-unauthenticated-shares": "Autoriser les partages anonymes",
"admin.config.share.allow-unauthenticated-shares.description": "Permet aux visiteurs de créer des partages",
"admin.config.share.max-expiration": "Échéance",
"admin.config.share.max-expiration.description": "Échéance du partage en heures. Indiquez 0 pour quil nexpire jamais.",
"admin.config.share.share-id-length": "Taille de l'identifiant généré",
"admin.config.share.share-id-length.description": "Taille par défaut de l'identifiant généré pour un partage. Cette valeur est aussi utilisée pour générer les liens des partages inverses. Une valeur inférieure à 8 n'est pas considérée sûre.",
"admin.config.share.max-size": "Taille max",
"admin.config.share.max-size.description": "Taille maximale du partage en octets",
"admin.config.share.zip-compression-level": "Niveau de compression",
@@ -344,10 +359,14 @@ export default {
"admin.config.smtp.password": "Mot de passe",
"admin.config.smtp.password.description": "Mot de passe du serveur SMTP",
"admin.config.smtp.button.test": "Envoyer un courriel de test",
"admin.config.smtp.allow-unauthorized-certificates": "Faire confiance aux certificats de serveurs SMTP non autorisés",
"admin.config.smtp.allow-unauthorized-certificates.description": "Ne permettez ceci que si vous avez besoin de faire confiance aux certificats autosignés.",
"admin.config.oauth.allow-registration": "Autoriser linscription",
"admin.config.oauth.allow-registration.description": "Permettre aux utilisateurs de sinscrire via leur identifiant social",
"admin.config.oauth.ignore-totp": "Ignorer TOTP",
"admin.config.oauth.ignore-totp.description": "Ignorer le TOTP lorsque lutilisateur utilise un identifiant social.",
"admin.config.oauth.ignore-totp": "Ignorer double authentification TOTP",
"admin.config.oauth.ignore-totp.description": "Ignorer la double-authentification (TOTP) lorsque lutilisateur utilise un identifiant social",
"admin.config.oauth.disable-password": "Désactiver la connexion par mot de passe",
"admin.config.oauth.disable-password.description": "Désactive la connexion par mot de passe\nAssurez-vous quun fournisseur OAuth soit correctement configuré avant dactiver cette configuration pour éviter d'être enfermé.",
"admin.config.oauth.github-enabled": "GitHub",
"admin.config.oauth.github-enabled.description": "Permettre la connexion via GitHub.",
"admin.config.oauth.github-client-id": "ID client de GitHub",
@@ -369,7 +388,9 @@ export default {
"admin.config.oauth.microsoft-client-secret": "Secret du client Microsoft",
"admin.config.oauth.microsoft-client-secret.description": "Le secret du client de lapplication Microsoft OAuth",
"admin.config.oauth.discord-enabled": "Discord",
"admin.config.oauth.discord-enabled.description": "Permettre la connexion via Discord.",
"admin.config.oauth.discord-enabled.description": "Permettre la connexion via Discord",
"admin.config.oauth.discord-limited-users": "Utilisateurs limités sur Discord",
"admin.config.oauth.discord-limited-users.description": "Limiter la connexion à des utilisateurs spécifiques par leur identifiant Discord. Laissez vide pour désactiver.",
"admin.config.oauth.discord-limited-guild": "ID de serveur restreint Discord",
"admin.config.oauth.discord-limited-guild.description": "Limiter la connexion aux utilisateurs sur un serveur spécifique. Laissez vide pour désactiver.",
"admin.config.oauth.discord-client-id": "ID du client Discord",
@@ -380,12 +401,41 @@ export default {
"admin.config.oauth.oidc-enabled.description": "Permettre la connexion via OpenID",
"admin.config.oauth.oidc-discovery-uri": "URI de découverte OpenID",
"admin.config.oauth.oidc-discovery-uri.description": "LURI de découverte de la connexion à l'application OpenID OAuth",
"admin.config.oauth.oidc-sign-out": "Déconnexion du fournisseur OpenID Connect",
"admin.config.oauth.oidc-sign-out.description": "Si activé, le bouton \"Déconnexion\" vous déconnectera également du fournisseur d'identité OpenID Connect",
"admin.config.oauth.oidc-scope": "OpenID Connect scope",
"admin.config.oauth.oidc-scope.description": "Scopes which should be requested from the OpenID Connect provider.",
"admin.config.oauth.oidc-username-claim": "Revendication du nom dutilisateur OpenID",
"admin.config.oauth.oidc-username-claim.description": "Le champ contenant la revendication du nom dutilisateur dans le jeton OpenID Connect. Laissez vide si vous ne savez pas quoi indiquer.",
"admin.config.oauth.oidc-role-path": "Chemin vers les rôles dans le jeton OpenID Connect",
"admin.config.oauth.oidc-role-path.description": "Doit être un chemin JMES valide référençant un tableau de rôles. " + "La gestion des droits d'accès en utilisant les rôles OpenID Connect n'est recommandée que si aucun autre fournisseur d'identité n'est configuré et que la connexion par mot de passe est désactivée. " + "Laissez vide si vous ne savez pas ce qu'est cette configuration.",
"admin.config.oauth.oidc-role-general-access": "Rôle OpenID Connect pour un accès général",
"admin.config.oauth.oidc-role-general-access.description": "Rôle requis pour un accès général. Doit être présent dans les rôles d'un utilisateur pour qu'il se connecte. " + "Laissez vide si vous ne savez pas ce qu'est cette configuration.",
"admin.config.oauth.oidc-role-admin-access": "Rôle OpenID Connect pour l'accès admin",
"admin.config.oauth.oidc-role-admin-access.description": "Rôle requis pour l'accès administratif. Doit être présent dans les rôles d'un utilisateur pour accéder au panneau d'administration. " + "Laissez vide si vous ne savez pas ce qu'est cette configuration.",
"admin.config.oauth.oidc-client-id": "ID du client OpenID",
"admin.config.oauth.oidc-client-id.description": "LID du client de lapplication OAuth OpenID Connect",
"admin.config.oauth.oidc-client-secret": "Secret du client OpenID",
"admin.config.oauth.oidc-client-secret.description": "Le secret du client de lapplication OAuth OpenID Connect",
"admin.config.category.ldap": "LDAP",
"admin.config.ldap.enabled": "Activer LDAP",
"admin.config.ldap.enabled.description": "Utiliser l'authentification LDAP pour la connexion de l'utilisateur",
"admin.config.ldap.url": "URL du serveur",
"admin.config.ldap.url.description": "URL du serveur LDAP",
"admin.config.ldap.bind-dn": "DN de liaison",
"admin.config.ldap.bind-dn.description": "Utilisateur par défaut utilisé pour effectuer la recherche de l'utilisateur",
"admin.config.ldap.bind-password": "Mot de passe de liaison",
"admin.config.ldap.bind-password.description": "Mot de passe utilisé pour effectuer la recherche de l'utilisateur",
"admin.config.ldap.search-base": "Base d'utilisateurs",
"admin.config.ldap.search-base.description": "Emplacement de base, où la recherche de l'utilisateur sera effectuée",
"admin.config.ldap.search-query": "Requête utilisateur",
"admin.config.ldap.search-query.description": "La requête utilisateur sera utilisée pour rechercher dans la base d'utilisateurs de l'utilisateur LDAP. %username% peut être utilisé comme espace réservé pour les entrées données par l'utilisateur.",
"admin.config.ldap.admin-groups": "Groupe administrateur",
"admin.config.ldap.admin-groups.description": "Un groupe est nécessaire pour un accès administratif.",
"admin.config.ldap.field-name-member-of": "Nom de l'attribut des groupes d'utilisateurs",
"admin.config.ldap.field-name-member-of.description": "Nom d'attribut LDAP pour les groupes dont un utilisateur est membre. Il est utilisé lors de la vérification du groupe d'administrateurs.",
"admin.config.ldap.field-name-email": "Nom d'attribut de l'e-mail de l'utilisateur",
"admin.config.ldap.field-name-email.description": "Nom d'attribut LDAP pour l'e-mail d'un utilisateur.",
// 404
"404.description": "Désolé, mais cette page nexiste pas.",
"404.button.home": "Retour à laccueil",
@@ -402,7 +452,7 @@ export default {
"error.msg.already_linked": "Le compte {0} est déjà associé à un autre compte.",
"error.msg.not_linked": "Le compte {0} nest pas encore associé à compte.",
"error.msg.unverified_account": "Le compte {0} n'est pas vérifié, veuillez réessayer après vérification.",
"error.msg.discord_guild_permission_denied": "Vous nêtes pas autorisé à vous authentifier.",
"error.msg.user_not_allowed": "Vous nêtes pas autorisé à vous authentifier.",
"error.msg.cannot_get_user_info": "Impossible dobtenir vos informations utilisateur à partir du compte {0}.",
"error.param.provider_github": "GitHub",
"error.param.provider_google": "Google",
@@ -421,8 +471,9 @@ export default {
"common.button.generate": "Générer",
"common.button.done": "Terminer",
"common.text.link": "Lien",
"common.text.navigate-to-link": "Accéder au lien",
"common.text.navigate-to-link": "Visiter le lien",
"common.text.or": "ou",
"common.text.redirecting": "Redirection...",
"common.button.go-back": "Précédent",
"common.button.go-home": "Accueil",
"common.notify.copied": "Votre lien a été copié dans le presse-papiers",
@@ -432,6 +483,8 @@ export default {
"common.error.invalid-email": "Adresse courriel invalide",
"common.error.too-short": "Doit comporter au moins {length} caractères",
"common.error.too-long": "Doit comporter au plus {length} caractères",
"common.error.number-too-small": "Doit être au moins {min}",
"common.error.number-too-large": "Doit être au plus {max}",
"common.error.exact-length": "Doit comporter exactement {length} caractères",
"common.error.invalid-number": "Doit être un nombre",
"common.error.field-required": "Ce champ est obligatoire"

View File

@@ -3,7 +3,7 @@ export default {
"navbar.upload": "Feltöltés",
"navbar.signin": "Bejelentkezés",
"navbar.home": "Home",
"navbar.signup": "Feliratkozás",
"navbar.signup": "Regisztráció",
"navbar.links.shares": "Megosztásaim",
"navbar.links.reverse": "Fordított megosztások",
"navbar.avatar.account": "Fiókom",
@@ -16,7 +16,7 @@ export default {
"home.bullet.a.name": "Saját üzemeltetés",
"home.bullet.a.description": "A Pingvin Share megosztót saját gépéről futtathatja.",
"home.bullet.b.name": "Adatvédelem",
"home.bullet.b.description": "A saját fájlok személyesek és semmi keresnivalójuk harmadik feleknél.",
"home.bullet.b.description": "A te fájljaid a tiéd, és harmadik felek soha nem férnek hozzá.",
"home.bullet.c.name": "Nincs bosszantó fájl méretkorlát",
"home.bullet.c.description": "Akkora fájlokat oszt meg, amekkorákat csak akar. Határt csak a lemezterület szab.",
"home.button.start": "Nosza, kezdjük",
@@ -34,6 +34,7 @@ export default {
"signIn.notify.totp-required.title": "Kétfaktoros hitelesítésre van szükség",
"signIn.notify.totp-required.description": "Adja meg a másik úton kapott kódját",
"signIn.oauth.or": "VAGY",
"signIn.oauth.signInWith": "Bejelentkezés a következővel",
"signIn.oauth.github": "GitHub",
"signIn.oauth.google": "Google",
"signIn.oauth.microsoft": "Microsoft",
@@ -57,12 +58,12 @@ export default {
// /auth/reset-password
"resetPassword.title": "Elfelejtette jelszavát?",
"resetPassword.description": "Adja meg emailcímét a jelszó alaphelyzetbe állításához.",
"resetPassword.notify.success": "A jelszó visszaállítására szolgáló linket tartalmazó üzenetet küldtünk, ha az e-mail létezik.",
"resetPassword.notify.success": "A jelszó visszaállítására szolgáló linket tartalmazó üzenetet küldtünk, abban az esetben, ha az e-mail létezik.",
"resetPassword.button.back": "Vissza a bejelentkezési oldalra",
"resetPassword.text.resetPassword": "Jelszó alaphelyzetbe állítása",
"resetPassword.text.enterNewPassword": "Adja meg új jelszavát",
"resetPassword.input.password": "Új jelszó",
"resetPassword.notify.passwordReset": "Sikeresen megadta az új jelszót.",
"resetPassword.notify.passwordReset": "A jelszavát sikeresen helyreállította.",
// /account
"account.title": "Fiókom",
"account.card.info.title": "Fiókinformáció",
@@ -84,7 +85,7 @@ export default {
"account.card.oauth.unlink": "Leválasztás",
"account.card.oauth.unlinked": "Leválasztva",
"account.modal.unlink.title": "Fiók leválasztása",
"account.modal.unlink.description": "A közösségi hálón keresztül csatolt fiók leválasztásával elveszítheti hozzáférését a fiókhoz, ha a név és jelszó kombinációra nem emlékszik.",
"account.modal.unlink.description": "A közösségi hálón keresztül csatolt fiók leválasztásával elveszítheti hozzáférését a fiókhoz, ha a név és jelszó kombinációra nem emlékszik",
"account.notify.oauth.unlinked.success": "Leválasztás sikeres",
"account.card.security.title": "Biztonság",
"account.card.security.totp.enable.description": "Adja meg az aktuális jelszavát a TOTP engedélyezéséhez",
@@ -120,12 +121,12 @@ export default {
"account.shares.table.name": "Megnevezés",
"account.shares.table.description": "Leírás",
"account.shares.table.visitors": "Látogatók",
"account.shares.table.expiresAt": "Lejárat",
"account.shares.table.createdAt": "Létrehozás időpontja",
"account.shares.table.expiresAt": "Lejár",
"account.shares.table.createdAt": "Létrehozva",
"account.shares.table.size": "Méret",
"account.shares.modal.share-informations": "Megosztás adatai",
"account.shares.modal.share-link": "Hivatkozás",
"account.shares.modal.delete.title": "Törlés: {share}",
"account.shares.modal.delete.title": "Megosztás törlése: {share}",
"account.shares.modal.delete.description": "Biztos abban, hogy törölni kívánja ezt a megosztást?",
// END /account/shares
// /account/reverseShares
@@ -149,8 +150,12 @@ export default {
"account.reverseShares.modal.expiration.year-singular": "év",
"account.reverseShares.modal.expiration.year-plural": "év",
"account.reverseShares.modal.max-size.label": "Megosztás megengedett max mérete",
"account.reverseShares.modal.send-email": "Email értesítés küldése",
"account.reverseShares.modal.send-email.description": "Email értesítés arról, amikor a fordított megosztási hivatkozást használva megosztást hoznak létre.",
"account.reverseShares.modal.send-email": "E-mail értesítések",
"account.reverseShares.modal.send-email.description": "E-mailben értesít, amikor a fordított megosztási hivatkozást használva megosztást hoznak létre.",
"account.reverseShares.modal.simplified": "Egyszerű mód",
"account.reverseShares.modal.simplified.description": "A fájlt feltöltő személy számára egyszerűsíthető az Önnel történő megosztás. A megosztásnak csak a neve és a leírása módosítható.",
"account.reverseShares.modal.public-access": "Nyilvános megosztás",
"account.reverseShares.modal.public-access.description": "A fordított megosztással létrehozott megosztások nyilvánossá tétele. Kikapcsolása esetén csak a megosztás létrehozója és Ön láthatja a megosztást.",
"account.reverseShares.modal.max-use.label": "Megengedett alkalmak száma",
"account.reverseShares.modal.max-use.description": "A megosztási hivatkozás megengedett felhasználási alkalmainak száma.",
"account.reverseShare.never-expires": "Ez a fordított megosztás soha nem fog lejárni.",
@@ -178,14 +183,14 @@ export default {
"admin.users.table.username": "Felhasználónév",
"admin.users.table.email": "Email",
"admin.users.table.admin": "Admin ügyek",
"admin.users.edit.update.title": "{username} felhasználó frissítése",
"admin.users.edit.update.title": "{username} felhasználó szerkesztése",
"admin.users.edit.update.admin-privileges": "Adminisztrátori jogosultságok",
"admin.users.edit.update.change-password.title": "Jelszó módosítása",
"admin.users.edit.update.change-password.field": "Új jelszó",
"admin.users.edit.update.change-password.button": "Új jelszó mentése",
"admin.users.edit.update.notify.password.success": "A jelszó sikeresen frissítésre került",
"admin.users.edit.delete.title": "{username} felhasználó törlése",
"admin.users.edit.delete.description": "Valóban törölni kívánja ezt a felhasználót és vele az ő minden egyes megosztását?",
"admin.users.edit.delete.title": "{username} felhasználó törlése ?",
"admin.users.edit.delete.description": "Biztosan törölni szeretné ezt a felhasználót és az összes megosztását?",
// showCreateUserModal.tsx
"admin.users.modal.create.title": "Felhasználó létrehozása",
"admin.users.modal.create.username": "Felhasználónév",
@@ -201,17 +206,20 @@ export default {
"admin.shares.table.id": "Megosztás ID",
"admin.shares.table.username": "Létrehozó",
"admin.shares.table.visitors": "Látogatók",
"admin.shares.table.expires": "Lejárat",
"admin.shares.edit.delete.title": "{id} megosztás törlése",
"admin.shares.table.expires": "Lejár",
"admin.shares.edit.delete.title": "Megosztás törlése: {id}",
"admin.shares.edit.delete.description": "Valóban törölni kívánja ezt a megosztást?",
// END /admin/shares
// /upload
"upload.title": "Feltöltés",
"upload.notify.confirm-leave": "Are you sure you want to leave this page? Your upload will be canceled.",
"upload.notify.generic-error": "Hiba lépett fel a megosztás befejezése közben.",
"upload.notify.count-failed": "{count} darab fájlt nem sikerült feltölteni. Próbálja ismét.",
"upload.reverse-share.error.invalid.title": "Invalid reverse share link",
"upload.reverse-share.error.invalid.description": "This reverse share has expired or is invalid.",
// Dropzone.tsx
"upload.dropzone.title": "Fájlok feltöltése",
"upload.dropzone.description": "Húzza ide a fájlokat a megosztás elkészítéséhez. Összesen legfeljebb {maxSize} fájl húzható egy megosztásba.",
"upload.dropzone.description": "Húzza ide a fájlokat a megosztás elkészítéséhez. Legfeljebb {maxSize} fájl húzható egy megosztásba.",
"upload.dropzone.notify.file-too-big": "A fájlok mérete meghaladja a megengedett {maxSize} méretet.",
// FileList.tsx
"upload.filelist.name": "Megnevezés",
@@ -223,8 +231,8 @@ export default {
"upload.modal.not-signed-in": "Nincs bejelentkezve",
"upload.modal.not-signed-in-description": "Nem lesz képes megosztását kézzel törölni vagy megtekinteni a látogatók számát.",
"upload.modal.expires.never": "soha",
"upload.modal.expires.never-long": "Nincs lejárat",
"upload.modal.expires.error.too-long": "A lejárat meghaladja a megengedett max {max} értéket.",
"upload.modal.expires.never-long": "Permanent share",
"upload.modal.expires.error.too-long": "Expiration date exceeds the maximum of {max}.",
"upload.modal.link.label": "Hivatkozás",
"upload.modal.expires.label": "Lejárat",
"upload.modal.expires.minute-singular": "perc",
@@ -254,6 +262,7 @@ export default {
"upload.modal.completed.never-expires": "Ez a megosztás soha nem fog lejárni.",
"upload.modal.completed.expires-on": "A megosztás lejárata: {expiration}.",
"upload.modal.completed.share-ready": "A megosztás készen áll",
"upload.modal.completed.notified-reverse-share-creator": "Értesítettük a fordított megosztás létrehozóját. Ezt a linket kézzel is megoszthatja velük más csatornákon keresztül.",
// END /upload
// /share/[id]
"share.title": "Megosztás: {shareId}",
@@ -263,17 +272,19 @@ export default {
"share.error.removed.title": "Korábban már eltávolított megosztás",
"share.error.not-found.title": "Fel nem lelhető megosztás",
"share.error.not-found.description": "A keresett megosztás nem létezik.",
"share.error.access-denied.title": "Privát megosztás",
"share.error.access-denied.description": "Ez a fiók nem rendelkezik jogosultsággal a megosztás eléréséhez",
"share.modal.password.title": "Jelszó szükséges",
"share.modal.password.description": "A megosztott tartalom eléréséhez adja meg a megosztás jelszavát.",
"share.modal.password.description": "Please enter the password to acces this share.",
"share.modal.password": "Jelszó",
"share.modal.error.invalid-password": "Érvénytelen jelszó",
"share.button.download-all": "Mindet letölti",
"share.notify.download-all-preparing": "A megosztás éppen előkészületben. Kérjük próbálja újra néhány perc múlva.",
"share.notify.download-all-preparing": "The share is being prepared. Please try again in a few minutes.",
"share.modal.file-link": "Fájl hivatkozás",
"share.table.name": "Megnevezés",
"share.table.size": "Méret",
"share.modal.file-preview.error.not-supported.title": "Az előnézetek nem támogatottak",
"share.modal.file-preview.error.not-supported.description": "E fájltípushoz nincsen támogatott előnézet. Töltse le a fájlt a megtekintéshez.",
"share.modal.file-preview.error.not-supported.description": "Previews are not supported for this type of files. Please download the file to view it.",
// END /share/[id]
// /share/[id]/edit
"share.edit.title": "Módosítás: {shareId}",
@@ -292,62 +303,70 @@ export default {
"admin.config.general.app-name.description": "Az alkalmazás neve",
"admin.config.general.app-url": "Alkalmazás URL hivatkozás",
"admin.config.general.app-url.description": "A Pingvin Share megosztáskezelőre mutató hivatkozás",
"admin.config.general.secure-cookies": "Secure cookies",
"admin.config.general.secure-cookies.description": "Whether to set the secure flag on cookies. If enabled, the site will not function when accessed over HTTP.",
"admin.config.general.show-home-page": "Kezdőlap mutatása",
"admin.config.general.show-home-page.description": "A kezdőlap mutatásának ki- és bekapcsolása",
"admin.config.general.session-duration": "Session Duration",
"admin.config.general.session-duration.description": "Time in hours after which a user must log in again (default: 3 months).",
"admin.config.general.session-duration": "Munkamenet időtartama",
"admin.config.general.session-duration.description": "Annak az időtartamnak a megadása, amit követően a felhasználónak ismét be kell jelentkeznie (alapérték: 3 hónap).",
"admin.config.general.logo": "Logó",
"admin.config.general.logo.description": "A logó személyessé tételéhez töltsön fel egy új képet. A formátum legyen PNG, az oldalarány 1:1.",
"admin.config.general.logo.placeholder": "Kép kiválasztása",
"admin.config.email.enable-share-email-recipients": "Megosztás emailben küldésének engedélyezése",
"admin.config.email.enable-share-email-recipients.description": "Email küldése a megosztásról a címzetteknek. Csak akkor engedélyezze, ha az SMTP már engedélyezett.",
"admin.config.email.enable-share-email-recipients": "Enable email recipient sharing",
"admin.config.email.enable-share-email-recipients.description": "Whether to allow email sharing with recipients. Only enable this if SMTP is activated.",
"admin.config.email.share-recipients-subject": "Megosztási email tárgya",
"admin.config.email.share-recipients-subject.description": "A megosztások címzettjeinek küldött email tárgya.",
"admin.config.email.share-recipients-message": "Megosztási email szövege",
"admin.config.email.share-recipients-message.description": "A megosztás címzettjeinek küldött email tartalma. Beépíthető változók:\n {creator} - A megosztás létrehozójának neve\n {shareUrl} - A megosztás URL hivatkozása\n {desc} - A megosztás ismertetése\n {expires} - A megosztás lejáratának dátuma\n A változók helyén az aktuális tartalom fog szerepelni.",
"admin.config.email.share-recipients-message.description": "Message which gets sent to the share recipients. Available variables:\n {creator} - The username of the creator of the share\n {creatorEmail} - The email of the creator of the share\n {shareUrl} - The URL of the share\n {desc} - The description of the share\n {expires} - The expiration date of the share\n These variables will be replaced with the actual value.",
"admin.config.email.reverse-share-subject": "Fordított megosztás tárgya",
"admin.config.email.reverse-share-subject.description": "Annak az emailnek a tárgya, ami a fordított megosztást használva készített megosztásról kerül kiküldésre.",
"admin.config.email.reverse-share-subject.description": "Subject of the sent email when someone created a share with your reverse share link.",
"admin.config.email.reverse-share-message": "Fordított megosztás üzenete",
"admin.config.email.reverse-share-message.description": "Az Ön által kiküldött fordított megosztási hivatkozás használatával készített megosztás emailjének szövege. A(z) {shareUrl} helyére a létrehozó neve és a megosztás URL hivatkozása kerül.",
"admin.config.email.reset-password-subject": "Jelszó visszaállítási tárgy",
"admin.config.email.reset-password-subject.description": "A felhasználó által kezdeményezett jelszó visszaállításkor küldött email tárgya.",
"admin.config.email.reset-password-subject.description": "Subject of the sent email when a user requests a password reset.",
"admin.config.email.reset-password-message": "Jelszó visszaállítási üzenet",
"admin.config.email.reset-password-message.description": "A felhasználó által kezdeményezett jelszó visszaállításkor küldött email szövege. A(z) {url} helyére a jelszó visszaállítási URL hivatkozás kerül.",
"admin.config.email.invite-subject": "Meghívó tárgya",
"admin.config.email.invite-subject.description": "Az adminisztrátortól a meghívott felhasználó felé küldött email tárgya.",
"admin.config.email.invite-subject.description": "Subject of the sent email when an admin invites a user.",
"admin.config.email.invite-message": "Meghívó szövege",
"admin.config.email.invite-message.description": "Az adminisztrátortól a meghívott felhasználó felé küldött email szövege. A(z) {url} helyére a meghívó URL hivatkozása kerül, a(z) {password} helyére a jelszó.",
"admin.config.email.invite-message.description": "Message which gets sent when an admin invites a user. {url} will be replaced with the invite URL, {email} with the email and {password} with the users password.",
"admin.config.share.allow-registration": "Regisztráció engedélyezése",
"admin.config.share.allow-registration.description": "Regisztráció engedélyezésének ki- és bekapcsolása",
"admin.config.share.allow-unauthenticated-shares": "Hitelesítés nélküli megosztások engedélyezése",
"admin.config.share.allow-unauthenticated-shares.description": "Hitelesítés nélküli felhasználók létre hozhatnak-e megosztásokat",
"admin.config.share.max-expiration": "Max lejárat",
"admin.config.share.max-expiration.description": "A megosztások megengedett leghosszabb lejárata órában. 0 értékkel a lejárat kikapcsolható.",
"admin.config.share.share-id-length": "Default share ID length",
"admin.config.share.share-id-length.description": "Default length for the generated ID of a share. This value is also used to generate links for reverse shares. A value below 8 is not considered secure.",
"admin.config.share.max-size": "Max méret",
"admin.config.share.max-size.description": "A megosztások megengedett legnagyobb mérete bájtban",
"admin.config.share.zip-compression-level": "Zip tömörítési szint",
"admin.config.share.zip-compression-level.description": "A fájlméret és a tömörítésre fordított idő közötti választás. Az érték 0 - 9 közül választható, 0: nincs tömörítés, 9: legnagyobb mértékű tömörítés. ",
"admin.config.share.chunk-size": "Darabolási méret",
"admin.config.share.chunk-size.description": "Az internet kapcsolat megbízhatóságának és átvitelének megfelelően választható feltöltési darabok mérete bájtban. A kisebb darabok növelhetik a szakadozó kapcsolatok használatának sikerességét, a nagyobb darabok a stabil kapcsolatokat gyorsítják.",
"admin.config.share.chunk-size.description": "Adjust the chunk size (in bytes) for your uploads to balance efficiency and reliability according to your internet connection. Smaller chunks can enhance success rates for unstable connections, while larger chunks make uploads faster for stable connections.",
"admin.config.share.auto-open-share-modal": "Megosztás létrehozása felület automatikus megjelenítése",
"admin.config.share.auto-open-share-modal.description": "A mgosztás létrehozása előugró elem automatikusan megjelenik a fájlok kiválasztásakor, ehhez nem szükséges külön kattintani.",
"admin.config.smtp.enabled": "Engedélyezett",
"admin.config.smtp.enabled": "Enable",
"admin.config.smtp.enabled.description": "A(z) SMTP bekapcsolása. Csak akkor engedélyezze, ha már megadta a kiszolgáló, port, email, felhasználónév és jelszó beállításokat az SMTP kiszolgálóhoz.",
"admin.config.smtp.host": "Kiszolgáló",
"admin.config.smtp.host.description": "Az SMTP szerver kiszolgáló gépe",
"admin.config.smtp.port": "Port",
"admin.config.smtp.port.description": "Az SMTP kiszolgáló portja",
"admin.config.smtp.email": "Email",
"admin.config.smtp.email.description": "Az emailek kiküldésére használt email cím",
"admin.config.smtp.email.description": "Email address from wich the emails get sent",
"admin.config.smtp.username": "Felhasználónév",
"admin.config.smtp.username.description": "Felhasználónév az SMTP kiszolgálón",
"admin.config.smtp.password": "Jelszó",
"admin.config.smtp.password.description": "Jelszó az SMTP kiszolgálón",
"admin.config.smtp.button.test": "Teszt email küldése",
"admin.config.smtp.allow-unauthorized-certificates": "A jogosulatlan SMTP kiszolgáló tanúsítványok is megbízhatók",
"admin.config.smtp.allow-unauthorized-certificates.description": "Csak akkor engedélyezze ha saját aláírású tanúsítványok elfogadása is szükséges.",
"admin.config.oauth.allow-registration": "Regisztráció engedélyezése",
"admin.config.oauth.allow-registration.description": "A felhasználók közösségi bejelentkezésen át is regisztrálhatnak",
"admin.config.oauth.ignore-totp": "TOTP mellőzése",
"admin.config.oauth.ignore-totp.description": "TOTP mellőzése a közösségi bejelentkezést használó felhasználónál",
"admin.config.oauth.disable-password": "Jelszavas bejelentkezés letiltása",
"admin.config.oauth.disable-password.description": "A jelszavas bejelentkezés be- és kikapcsolása\nA letiltás előtt a kizáródás elkerülésére mindenképpen ellenőrizendő az OAuth szolgáltató megfelelő konfigurációja.",
"admin.config.oauth.github-enabled": "GitHub",
"admin.config.oauth.github-enabled.description": "GitHub bejelentkezés engedélyezése",
"admin.config.oauth.github-client-id": "GitHub ügyfél ID",
@@ -370,6 +389,8 @@ export default {
"admin.config.oauth.microsoft-client-secret.description": "A Microsoft Oauth applikáció ügyfél titka",
"admin.config.oauth.discord-enabled": "Discord",
"admin.config.oauth.discord-enabled.description": "Discord bejelentkezés engedélyezése",
"admin.config.oauth.discord-limited-users": "Discord limited users",
"admin.config.oauth.discord-limited-users.description": "Limit signing in to specific users by their Discord ID. Leave it blank to disable.",
"admin.config.oauth.discord-limited-guild": "Discord limitált kiszolgáló ID azonosítója",
"admin.config.oauth.discord-limited-guild.description": "A kiszolgálón engedélyezett bejelentkezett felhasználók száma. Hagyja üresen a kikapcsoláshoz.",
"admin.config.oauth.discord-client-id": "Discord ügyfél ID azonosító",
@@ -380,12 +401,41 @@ export default {
"admin.config.oauth.oidc-enabled.description": "OpenID Connect bejelentkezés engedélyezése",
"admin.config.oauth.oidc-discovery-uri": "OpenID Connect Discovery URI",
"admin.config.oauth.oidc-discovery-uri.description": "Az OpenID Connect OAuth applikáció Discovery URI azonosítója",
"admin.config.oauth.oidc-sign-out": "Sign out from OpenID Connect",
"admin.config.oauth.oidc-sign-out.description": "Whether the “Sign out” button will sign out from the OpenID Connect provider",
"admin.config.oauth.oidc-scope": "OpenID Connect scope",
"admin.config.oauth.oidc-scope.description": "Scopes which should be requested from the OpenID Connect provider.",
"admin.config.oauth.oidc-username-claim": "OpenID Connect felhasználónév igény",
"admin.config.oauth.oidc-username-claim.description": "Az OpenID Connect ID token felhasználónév igénye. Hagyja üresen ha nincs információja a beállításról.",
"admin.config.oauth.oidc-role-path": "Az OpenID Connect token szerepeinek elérési útvonala",
"admin.config.oauth.oidc-role-path.description": "Szerepkörökből álló tömbre hivatkozó érvényes JMES-útvonalnak kell lennie. " + "A belépési jogosultságok kezelésére az OpenID Connect szerepkörök csak más azonosító szolgáltatások hiányában és a jelszavas bejelentkezés letiltottsága mellett javasolt. " + "Hagyja üresen, ha nem tudja, mi ez a konfiguráció.",
"admin.config.oauth.oidc-role-general-access": "OpenID Connect szerepkör általános hozzáféréshez",
"admin.config.oauth.oidc-role-general-access.description": "Role required for general access. Must be present in a users roles for them to log in. " + "Hagyja üresen, ha nem tudja, mi ez a konfiguráció.",
"admin.config.oauth.oidc-role-admin-access": "OpenID Connect szerepkör admin hozzáféréshez",
"admin.config.oauth.oidc-role-admin-access.description": "A rendszergazdai hozzáféréshez szükséges szerepkör. Meg kell lennie a felhasználó szerepkörében ahhoz, hogy hozzáférhessen az adminisztrációs panelhez. " + "Hagyja üresen, ha nem tudja, mi ez a konfiguráció.",
"admin.config.oauth.oidc-client-id": "OpenID Connect ügyfél ID azonosító",
"admin.config.oauth.oidc-client-id.description": "Az OpenID Connect OAuth applikáció ügyfél ID azonosítója",
"admin.config.oauth.oidc-client-secret": "OpenID Connect ügyfél titok",
"admin.config.oauth.oidc-client-secret.description": "Az OpenID Connect OAuth applikáció ügyfél titka",
"admin.config.category.ldap": "LDAP",
"admin.config.ldap.enabled": "Enable LDAP",
"admin.config.ldap.enabled.description": "LDAP hitelesítés használata a felhasználói beléptetéshez",
"admin.config.ldap.url": "Kiszolgáló URL",
"admin.config.ldap.url.description": "Az LDAP kiszolgáló URL címe",
"admin.config.ldap.bind-dn": "DN csatolása",
"admin.config.ldap.bind-dn.description": "Default user used to perform the user search",
"admin.config.ldap.bind-password": "Jelszó csatolása",
"admin.config.ldap.bind-password.description": "Password used to perform the user search",
"admin.config.ldap.search-base": "Felhasználóbázis",
"admin.config.ldap.search-base.description": "A felhasználókeresés végrehajtásának helye",
"admin.config.ldap.search-query": "Felhasználó lekérdezés",
"admin.config.ldap.search-query.description": "A felhasználó lekérdezés kísérli meg az LDAP felhasználó elérését a felhasználóbázisban. %username% helyettesítheti az adott felhasználónevet.",
"admin.config.ldap.admin-groups": "Admin csoport",
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
"admin.config.ldap.field-name-member-of": "User groups attribute name",
"admin.config.ldap.field-name-member-of.description": "LDAP attribute name for the groups, an user is a member of. This is used when checking for the admin group.",
"admin.config.ldap.field-name-email": "User email attribute name",
"admin.config.ldap.field-name-email.description": "LDAP attribute name for the email of an user.",
// 404
"404.description": "Hoppá - ez az oldal nem létezik.",
"404.button.home": "Vissza a Kezdőlapra",
@@ -400,10 +450,10 @@ export default {
"error.msg.no_user": "A(z) {0} fiókhoz kapcsolt felhasználó nem létezik.",
"error.msg.no_email": "Nem nyerhető ki a(z) {0} fiók emailcíme.",
"error.msg.already_linked": "Ez a(z) {0} fiók már kapcsolódik egy másik fiókhoz.",
"error.msg.not_linked": "Ez a(z){0} fiók még nem kapcsolódott egyetlen fiókhoz sem.",
"error.msg.not_linked": "This {0} account hasn't been linked to any account yet.",
"error.msg.unverified_account": "Ezt a(z) {0} fiókot még nem igazolták vissza, kérem próbálja újra a megerősítés után.",
"error.msg.discord_guild_permission_denied": "Nem jelentkezhet be.",
"error.msg.cannot_get_user_info": "Nem nyerhető ki felhasználói információ ebből a(z) {0} fiókból.",
"error.msg.user_not_allowed": "Nem jelentkezhet be.",
"error.msg.cannot_get_user_info": "Cannot get your user info from this {0} account.",
"error.param.provider_github": "GitHub",
"error.param.provider_google": "Google",
"error.param.provider_microsoft": "Microsoft",
@@ -421,8 +471,9 @@ export default {
"common.button.generate": "Létrehozás",
"common.button.done": "Kész",
"common.text.link": "Hivatkozás",
"common.text.navigate-to-link": "Ugrás a hivatkozásra",
"common.text.navigate-to-link": "Visit link",
"common.text.or": "vagy",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "Vissza",
"common.button.go-home": "Kezdőlap",
"common.notify.copied": "A hivatkozást a Vágólapra másoltuk",
@@ -432,6 +483,8 @@ export default {
"common.error.invalid-email": "Érvénytelen emailcím",
"common.error.too-short": "Legalább {length} karakter kell",
"common.error.too-long": "Legfejlebb {length} karakter adható meg",
"common.error.number-too-small": "Must be at least {min}",
"common.error.number-too-large": "Must be at most {max}",
"common.error.exact-length": "Pontosan {length} karakter szükséges",
"common.error.invalid-number": "Számot kell megadnia",
"common.error.field-required": "Ez egy kötelező mező"

Some files were not shown because too many files have changed in this diff Show More