Compare commits

...

35 Commits

Author SHA1 Message Date
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
66 changed files with 4411 additions and 3392 deletions

View File

@@ -1,3 +1,45 @@
## [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)

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

@@ -46,7 +46,7 @@ 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 ./Caddyfile /etc/caddy/Caddyfile
COPY ./reverse-proxy /etc/caddy
COPY ./scripts/docker-entrypoint.sh /opt/app/docker-entrypoint.sh
WORKDIR /opt/app

View File

@@ -13,6 +13,8 @@ Pingvin Share is a self-hosted file sharing platform and an alternative for WeTr
- 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
@@ -31,6 +33,19 @@ Pingvin Share is a self-hosted file sharing platform and an alternative for WeTr
The website is now listening on `http://localhost:3000`, have fun with Pingvin Share 🐧!
> [!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.
## 📚 Documentation
For more installation options and advanced configurations, please refer to the [documentation](https://stonith404.github.io/pingvin-share).
## 🖤 Contribute
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).
## ❤️ Sponsors
Thank you for supporting Pingvin Share 🙏
- [@COMPLEXWASTAKEN](https://github.com/COMPLEXWASTAKEN)

2045
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": "1.0.2",
"version": "1.1.2",
"scripts": {
"build": "nest build",
"dev": "cross-env NODE_ENV=development nest start --watch",
@@ -14,23 +14,23 @@
},
"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",
"@types/ldapjs": "^3.0.6",
"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",
@@ -40,48 +40,48 @@
"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",
"@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

@@ -173,11 +173,17 @@ export class AuthController {
@Res({ passthrough: true }) response: Response,
) {
await this.authService.signOut(request.cookies.access_token);
response.cookie("access_token", "accessToken", { maxAge: -1 });
const isSecure = this.config.get("general.appUrl").startsWith("https");
response.cookie("access_token", "accessToken", {
maxAge: -1,
secure: isSecure,
});
response.cookie("refresh_token", "", {
path: "/api/auth/token",
httpOnly: true,
maxAge: -1,
secure: isSecure,
});
}

View File

@@ -20,4 +20,4 @@ import { UserModule } from "../user/user.module";
providers: [AuthService, AuthTotpService, JwtStrategy, LdapService],
exports: [AuthService],
})
export class AuthModule { }
export class AuthModule {}

View File

@@ -29,7 +29,7 @@ export class AuthService {
private emailService: EmailService,
private ldapService: LdapService,
private userService: UserSevice,
) { }
) {}
private readonly logger = new Logger(AuthService.name);
async signUp(dto: AuthRegisterDTO, ip: string, isAdmin?: boolean) {
@@ -76,18 +76,28 @@ export class AuthService {
},
});
if (user?.password && await argon.verify(user.password, dto.password)) {
this.logger.log(`Successful password login for user ${user.email} from IP ${ip}`);
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);
}
}
if (this.config.get("ldap.enabled")) {
this.logger.debug(`Trying LDAP login for user ${dto.username}`);
const ldapUser = await this.ldapService.authenticateUser(dto.username, dto.password);
const ldapUser = await this.ldapService.authenticateUser(
dto.username,
dto.password,
);
if (ldapUser) {
const user = await this.userService.findOrCreateFromLDAP(dto.username, ldapUser);
this.logger.log(`Successful LDAP login for user ${user.email} from IP ${ip}`);
const user = await this.userService.findOrCreateFromLDAP(
dto.username,
ldapUser,
);
this.logger.log(
`Successful LDAP login for user ${user.email} from IP ${ip}`,
);
return this.generateToken(user);
}
}
@@ -262,9 +272,11 @@ export class AuthService {
refreshToken?: string,
accessToken?: string,
) {
const isSecure = this.config.get("general.appUrl").startsWith("https");
if (accessToken)
response.cookie("access_token", accessToken, {
sameSite: "lax",
secure: isSecure,
maxAge: 1000 * 60 * 60 * 24 * 30 * 3, // 3 months
});
if (refreshToken)
@@ -272,6 +284,7 @@ export class AuthService {
path: "/api/auth/token",
httpOnly: true,
sameSite: "strict",
secure: isSecure,
maxAge: 1000 * 60 * 60 * this.config.get("general.sessionDuration"),
});
}

View File

@@ -1,154 +1,194 @@
import { Inject, Injectable, Logger } from "@nestjs/common";
import * as ldap from "ldapjs";
import { AttributeJson, InvalidCredentialsError, SearchCallbackResponse, SearchOptions } from "ldapjs";
import {
AttributeJson,
InvalidCredentialsError,
SearchCallbackResponse,
SearchOptions,
} from "ldapjs";
import { inspect } from "node:util";
import { ConfigService } from "../config/config.service";
type LdapSearchEntry = {
objectName: string,
attributes: AttributeJson[],
objectName: string;
attributes: AttributeJson[];
};
async function ldapExecuteSearch(client: ldap.Client, base: string, options: SearchOptions): Promise<LdapSearchEntry[]> {
const searchResponse = await new Promise<SearchCallbackResponse>((resolve, reject) => {
client.search(base, options, (err, res) => {
if (err) {
reject(err);
} else {
resolve(res);
}
});
});
async function ldapExecuteSearch(
client: ldap.Client,
base: string,
options: SearchOptions,
): Promise<LdapSearchEntry[]> {
const searchResponse = await new Promise<SearchCallbackResponse>(
(resolve, reject) => {
client.search(base, options, (err, res) => {
if (err) {
reject(err);
} else {
resolve(res);
}
});
},
);
return await new Promise<any[]>((resolve, reject) => {
const entries: LdapSearchEntry[] = [];
searchResponse.on("searchEntry", entry => entries.push({ attributes: entry.pojo.attributes, objectName: entry.pojo.objectName }));
searchResponse.once("error", reject);
searchResponse.once("end", () => resolve(entries));
});
return await new Promise<any[]>((resolve, reject) => {
const entries: LdapSearchEntry[] = [];
searchResponse.on("searchEntry", (entry) =>
entries.push({
attributes: entry.pojo.attributes,
objectName: entry.pojo.objectName,
}),
);
searchResponse.once("error", reject);
searchResponse.once("end", () => resolve(entries));
});
}
async function ldapBindUser(client: ldap.Client, dn: string, password: string): Promise<void> {
return new Promise<void>((resolve, reject) => {
client.bind(dn, password, error => {
if (error) {
reject(error);
} else {
resolve();
}
});
})
async function ldapBindUser(
client: ldap.Client,
dn: string,
password: string,
): Promise<void> {
return new Promise<void>((resolve, reject) => {
client.bind(dn, password, (error) => {
if (error) {
reject(error);
} else {
resolve();
}
});
});
}
async function ldapCreateConnection(logger: Logger, url: string): Promise<ldap.Client> {
const ldapClient = ldap.createClient({
url: url.split(","),
connectTimeout: 10_000,
timeout: 10_000
});
async function ldapCreateConnection(
logger: Logger,
url: string,
): Promise<ldap.Client> {
const ldapClient = ldap.createClient({
url: url.split(","),
connectTimeout: 10_000,
timeout: 10_000,
});
await new Promise((resolve, reject) => {
ldapClient.once("error", reject);
ldapClient.on("setupError", reject);
ldapClient.on("socketTimeout", reject);
ldapClient.on("connectRefused", () => reject(new Error("connection has been refused")));
ldapClient.on("connectTimeout", () => reject(new Error("connect timed out")));
ldapClient.on("connectError", reject);
await new Promise((resolve, reject) => {
ldapClient.once("error", reject);
ldapClient.on("setupError", reject);
ldapClient.on("socketTimeout", reject);
ldapClient.on("connectRefused", () =>
reject(new Error("connection has been refused")),
);
ldapClient.on("connectTimeout", () =>
reject(new Error("connect timed out")),
);
ldapClient.on("connectError", reject);
ldapClient.on("connect", resolve);
}).catch(error => {
logger.error(`Connect error: ${inspect(error)}`);
ldapClient.destroy();
throw error;
});
ldapClient.on("connect", resolve);
}).catch((error) => {
logger.error(`Connect error: ${inspect(error)}`);
ldapClient.destroy();
throw error;
});
return ldapClient;
return ldapClient;
}
export type LdapAuthenticateResult = {
userDn: string,
attributes: Record<string, string[]>
userDn: string;
attributes: Record<string, string[]>;
};
@Injectable()
export class LdapService {
private readonly logger = new Logger(LdapService.name);
constructor(
@Inject(ConfigService)
private readonly serviceConfig: ConfigService,
) { }
private readonly logger = new Logger(LdapService.name);
constructor(
@Inject(ConfigService)
private readonly serviceConfig: ConfigService,
) {}
private async createLdapConnection(): Promise<ldap.Client> {
const ldapUrl = this.serviceConfig.get("ldap.url");
if (!ldapUrl) {
throw new Error("LDAP server URL is not defined");
}
const ldapClient = await ldapCreateConnection(this.logger, ldapUrl);
try {
const bindDn = this.serviceConfig.get("ldap.bindDn") || null;
if (bindDn) {
try {
await ldapBindUser(ldapClient, 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;
} catch (error) {
ldapClient.destroy();
throw error;
}
private async createLdapConnection(): Promise<ldap.Client> {
const ldapUrl = this.serviceConfig.get("ldap.url");
if (!ldapUrl) {
throw new Error("LDAP server URL is not defined");
}
public async authenticateUser(username: string, password: string): Promise<LdapAuthenticateResult | null> {
if (!username.match(/^[a-zA-Z0-0]+$/)) {
return null;
}
const searchBase = this.serviceConfig.get("ldap.searchBase");
const searchQuery = this.serviceConfig.get("ldap.searchQuery")
.replaceAll("%username%", username);
const ldapClient = await this.createLdapConnection();
const ldapClient = await ldapCreateConnection(this.logger, ldapUrl);
try {
const bindDn = this.serviceConfig.get("ldap.bindDn") || null;
if (bindDn) {
try {
const [result] = await ldapExecuteSearch(ldapClient, searchBase, {
filter: searchQuery,
scope: "sub"
});
if (!result) {
/* user not found */
return null;
}
try {
await ldapBindUser(ldapClient, result.objectName, password);
/*
* In theory we could query the user attributes now,
* but as we must query the user attributes for validation anyways
* we'll create a second ldap server connection.
*/
return {
userDn: result.objectName,
attributes: Object.fromEntries(result.attributes.map(attribute => [attribute.type, attribute.values])),
};
} catch (error) {
if (error instanceof InvalidCredentialsError) {
return null;
}
this.logger.warn(`LDAP user bind failure: ${inspect(error)}`);
return null;
} finally {
ldapClient.destroy();
}
await ldapBindUser(
ldapClient,
bindDn,
this.serviceConfig.get("ldap.bindPassword"),
);
} catch (error) {
this.logger.warn(`LDAP connect error: ${inspect(error)}`);
return null;
this.logger.warn(`Failed to bind to default user: ${error}`);
throw new Error("failed to bind to default user");
}
}
return ldapClient;
} catch (error) {
ldapClient.destroy();
throw error;
}
}
}
public async authenticateUser(
username: string,
password: string,
): Promise<LdapAuthenticateResult | null> {
if (!username.match(/^[a-zA-Z0-0]+$/)) {
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 [result] = await ldapExecuteSearch(ldapClient, searchBase, {
filter: searchQuery,
scope: "sub",
});
if (!result) {
/* user not found */
return null;
}
try {
await ldapBindUser(ldapClient, result.objectName, password);
/*
* In theory we could query the user attributes now,
* but as we must query the user attributes for validation anyways
* we'll create a second ldap server connection.
*/
return {
userDn: result.objectName,
attributes: Object.fromEntries(
result.attributes.map((attribute) => [
attribute.type,
attribute.values,
]),
),
};
} catch (error) {
if (error instanceof InvalidCredentialsError) {
return null;
}
this.logger.warn(`LDAP user bind failure: ${inspect(error)}`);
return null;
} finally {
ldapClient.destroy();
}
} catch (error) {
this.logger.warn(`LDAP connect error: ${inspect(error)}`);
return null;
}
}
}

View File

@@ -17,14 +17,15 @@ 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",

View File

@@ -51,7 +51,7 @@ export class OAuthService {
await this.updateIsAdmin(user);
const updatedUser = await this.prisma.user.findFirst({
where: {
email: user.email,
id: oauthUser.userId,
},
});
this.logger.log(`Successful login for user ${user.email} from IP ${ip}`);

View File

@@ -1,13 +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;
@@ -116,7 +116,13 @@ export abstract class GenericOidcProvider implements OAuthProvider<OidcToken> {
},
): 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);

View File

@@ -34,7 +34,9 @@ export class UserDTO {
totpVerified: boolean;
from(partial: Partial<UserDTO>) {
const result = plainToClass(UserDTO, partial, { excludeExtraneousValues: true });
const result = plainToClass(UserDTO, partial, {
excludeExtraneousValues: true,
});
result.isLdap = partial.ldapDN?.length > 0;
return result;
}

View File

@@ -19,10 +19,14 @@ import { UpdateOwnUserDTO } from "./dto/updateOwnUser.dto";
import { UpdateUserDto } from "./dto/updateUser.dto";
import { UserDTO } from "./dto/user.dto";
import { UserSevice } from "./user.service";
import { ConfigService } from "../config/config.service";
@Controller("users")
export class UserController {
constructor(private userService: UserSevice) {}
constructor(
private userService: UserSevice,
private config: ConfigService,
) {}
// Own user operations
@Get("me")
@@ -49,11 +53,17 @@ export class UserController {
@GetUser() user: User,
@Res({ passthrough: true }) response: Response,
) {
response.cookie("access_token", "accessToken", { maxAge: -1 });
const isSecure = this.config.get("general.appUrl").startsWith("https");
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));
}

View File

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

View File

@@ -17,7 +17,7 @@ export class UserSevice {
private emailService: EmailService,
private fileService: FileService,
private configService: ConfigService,
) { }
) {}
async list() {
return await this.prisma.user.findMany();
@@ -94,7 +94,9 @@ export class UserSevice {
async findOrCreateFromLDAP(username: string, ldap: LdapAuthenticateResult) {
const passwordHash = await argon.hash(crypto.randomUUID());
const userEmail = ldap.attributes["userPrincipalName"]?.at(0) ?? `${crypto.randomUUID()}@ldap.local`;
const userEmail =
ldap.attributes["userPrincipalName"]?.at(0) ??
`${crypto.randomUUID()}@ldap.local`;
const adminGroup = this.configService.get("ldap.adminGroups");
const isAdmin = ldap.attributes["memberOf"]?.includes(adminGroup) ?? false;
try {
@@ -114,8 +116,8 @@ export class UserSevice {
ldapDN: ldap.userDn,
},
where: {
ldapDN: ldap.userDn
}
ldapDN: ldap.userDn,
},
});
} catch (e) {
if (e instanceof PrismaClientKnownRequestError) {

View File

@@ -4,15 +4,11 @@ services:
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

@@ -4,25 +4,53 @@ id: configuration
# Configuration
You can customize Pingvin Share like changing your domain by going to the configuration page in your admin dashboard `/admin/config`.
You can customize Pingvin Share by going to the configuration page in your admin dashboard `/admin/config`.
#### Environment variables
## 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
#### 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. |
| 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` 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
#### 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

@@ -37,9 +37,9 @@ 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
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 `./Caddyfile`.
**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

@@ -4,12 +4,39 @@ id: integrations
# Integrations
#### ClamAV (Docker only)
## ClamAV
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.
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"
Please note that ClamAV needs a lot of [ressources](https://docs.clamav.net/manual/Installing/Docker.html#memory-ram-requirements).
### Stand-Alone
1. Install ClamAV
2. Specify the `CLAMAV_HOST` environment variable for the backend and restart the Pingvin Share backend.

View File

@@ -39,6 +39,6 @@ docker compose up -d
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
```
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````

View File

@@ -7,7 +7,7 @@ const config: Config = {
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",
@@ -37,7 +37,7 @@ const config: Config = {
themeConfig: {
image: "img/pingvinshare.svg",
colorMode:{
colorMode: {
respectPrefersColorScheme: true,
},
navbar: {

828
docs/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@
"start": "docusaurus start",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"deploy": "GIT_USER=stonith404 docusaurus deploy",
"clear": "docusaurus clear",
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
@@ -15,19 +15,19 @@
"typecheck": "tsc"
},
"dependencies": {
"@docusaurus/core": "3.4.0",
"@docusaurus/preset-classic": "3.4.0",
"@mdx-js/react": "^3.0.0",
"clsx": "^2.0.0",
"prism-react-renderer": "^2.3.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
"@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.4.0",
"@docusaurus/tsconfig": "3.4.0",
"@docusaurus/types": "3.4.0",
"typescript": "~5.2.2"
"@docusaurus/module-type-aliases": "3.5.2",
"@docusaurus/tsconfig": "3.5.2",
"@docusaurus/types": "3.5.2",
"typescript": "~5.6.2"
},
"browserslist": {
"production": [

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.

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "pingvin-share-frontend",
"version": "1.0.2",
"version": "1.1.2",
"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,37 +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": "^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

@@ -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,8 +25,8 @@ 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: {
@@ -74,10 +75,14 @@ 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")),
emailOrUsername: config.get("ldap.enabled")
? yup.string().matches(/^[^@]+$/, t("signIn.error.invalid-username"))
: yup.string().required(t("common.error.field-required")),
password: yup
.string()
.min(8, t("common.error.too-short", { length: 8 }))
@@ -118,15 +123,36 @@ 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(config.get("general.appUrl"), 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}>
@@ -148,8 +174,16 @@ const SignInForm = ({ redirectPath }: { redirectPath: string }) => {
})}
>
<TextInput
label={t("signin.input.email-or-username")}
placeholder={t("signin.input.email-or-username.placeholder")}
label={
config.get("ldap.enabled")
? t("signup.input.username")
: t("signin.input.email-or-username")
}
placeholder={
config.get("ldap.enabled")
? t("signup.input.username.placeholder")
: t("signin.input.email-or-username.placeholder")
}
{...form.getInputProps("emailOrUsername")}
/>
<PasswordInput
@@ -170,7 +204,7 @@ const SignInForm = ({ redirectPath }: { redirectPath: string }) => {
</Button>
</form>
)}
{oauth.length > 0 && (
{oauthProviders.length > 0 && (
<Stack mt={config.get("oauth.disablePassword") ? undefined : "xl"}>
{config.get("oauth.disablePassword") ? (
<Group align="center" className={classes.signInWith}>
@@ -182,7 +216,7 @@ const SignInForm = ({ redirectPath }: { redirectPath: string }) => {
</Group>
)}
<Group position="center">
{oauth.map((provider) => (
{oauthProviders.map((provider) => (
<Button
key={provider}
component="a"

View File

@@ -370,8 +370,7 @@ const CreateUploadModalBody = ({
placeholder={t("upload.modal.accordion.email.placeholder")}
searchable
creatable
id="recipient_email"
autoComplete="email"
id="recipient-emails"
type="email"
getCreateLabel={(query) => `+ ${query}`}
onCreate={(query) => {
@@ -426,7 +425,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

View File

@@ -24,6 +24,7 @@ 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";
export const LOCALES = {
ENGLISH: {
@@ -156,4 +157,9 @@ export const LOCALES = {
code: "vi-VN",
messages: viatnamese,
},
BULGARIAN: {
name: "Български",
code: "bg-BG",
messages: bulgarian,
},
};

View File

@@ -40,6 +40,7 @@ export default {
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
"signIn.error.invalid-username": "Invalid username",
// END /auth/signin
// /auth/signup
"signup.title": "أنشئ حسابًا",
@@ -456,6 +457,7 @@ export default {
"common.text.link": "الرابط",
"common.text.navigate-to-link": "الذهاب إلى الرابط",
"common.text.or": "أو",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "العودة",
"common.button.go-home": "العودة للصفحة الرئيسية",
"common.notify.copied": "تم نسخ الرابط إلى الحافظة",

View File

@@ -0,0 +1,473 @@
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 your files and should never get into the hands of 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.",
"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",
"signIn.error.invalid-username": "Invalid username",
// 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": "В случай, че имейлът съществува ще бъде изпратено съобщение с връзка за възстановяване на паролата.",
"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 reset successfully.",
// /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 don't have a password set. If you want to sign in with email and password you need to set 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 username and password.",
"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 at",
"account.shares.table.createdAt": "Created at",
"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 notification",
"account.reverseShares.modal.send-email.description": "Send 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 be able to customize only the name and description of the share.",
"account.reverseShares.modal.public-access": "Public access",
"account.reverseShares.modal.public-access.description": "Make the created shares with this reverse share public. If disabled, only you and the creator of the share can 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": "Update 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 his 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 At",
"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.generic-error": "An error occurred while finishing your share.",
"upload.notify.count-failed": "{count} files failed to upload. Trying again.",
// 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.",
"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": "Never Expires",
"upload.modal.expires.error.too-long": "Expiration exceeds maximum expiration date 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": "To access this share please enter the password for the 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.",
"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": "A preview for this file type is unsupported. 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.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 share email recipients",
"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.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.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-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-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.",
"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 password of the user.",
"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.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.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": "Enabled",
"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 which the emails get sent from",
"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-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-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": "Enabled 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 which will be used to execute the user search",
"admin.config.ldap.bind-password": "Bind password",
"admin.config.ldap.bind-password.description": "Password for the user search user",
"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.",
// 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 haven't 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": "Can not 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": "Go to the 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.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

@@ -4,8 +4,8 @@ export default {
"navbar.signin": "Přihlásit se",
"navbar.home": "Domů",
"navbar.signup": "Zaregistrovat se",
"navbar.links.shares": "Mé sdílení",
"navbar.links.reverse": "Zpětné sdílení",
"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",
@@ -40,6 +40,7 @@ export default {
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
"signIn.error.invalid-username": "Invalid username",
// END /auth/signin
// /auth/signup
"signup.title": "Vytvořit účet",
@@ -90,50 +91,50 @@ export default {
"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": "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",
"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": "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?",
"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": "My shares",
"account.shares.title.empty": "It's empty here 👀",
"account.shares.description.empty": "You don't have any 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": "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.size": "Size",
"account.shares.table.name": "Název",
"account.shares.table.description": "Popis",
"account.shares.table.visitors": "Návštěvníci",
"account.shares.table.expiresAt": "Vyprší",
"account.shares.table.createdAt": "Vytvořeno",
"account.shares.table.size": "Velikost",
"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?",
"account.shares.modal.share-link": "Odkaz na sdílení",
"account.shares.modal.delete.title": "Odstranit sdílení {share}",
"account.shares.modal.delete.description": "Opravdu chcete odstranit toto sdílení?",
// END /account/shares
// /account/reverseShares
"account.reverseShares.title": "Reverse shares",
"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": "You don't have any reverse shares.",
"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",
@@ -146,18 +147,18 @@ export default {
"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": "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 notification",
"account.reverseShares.modal.send-email.description": "Send 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 be able to customize only the name and description of the share.",
"account.reverseShares.modal.public-access": "Public access",
"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": "Odeslat oznámení e-mailem",
"account.reverseShares.modal.send-email.description": "Odeslat e-mailové upozornění, pokud je s tímto odkazem pro zpětné sdílení vytvořeno sdílení.",
"account.reverseShares.modal.simplified": "Zjednodušený režim",
"account.reverseShares.modal.simplified.description": "Usnadněte osobě, která nahrála soubor, sdílet jej s vámi. Budou moci upravit pouze název a popis sdílení.",
"account.reverseShares.modal.public-access": "Veřejný přístup",
"account.reverseShares.modal.public-access.description": "Make the created shares with this reverse share public. If disabled, only you and the creator of the share can 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.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í",
@@ -219,31 +220,31 @@ export default {
"upload.dropzone.description": "Pro zahájení sdílení přetáhněte soubory sem. Můžeme přijmout pouze soubory, které jsou menší než {maxSize}.",
"upload.dropzone.notify.file-too-big": "Vaše soubory přesahují maximální velikost {maxSize}.",
// FileList.tsx
"upload.filelist.name": "Name",
"upload.filelist.name": "Název",
"upload.filelist.size": "Velikost",
// showCreateUploadModal.tsx
"upload.modal.title": "Create Share",
"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": "never",
"upload.modal.expires.never": "nikdy",
"upload.modal.expires.never-long": "Nikdy nevyprší",
"upload.modal.expires.error.too-long": "Expiration exceeds maximum expiration date of {max}.",
"upload.modal.expires.error.too-long": "Platnosti přesahuje maximální datum vypršení platnosti {max}.",
"upload.modal.link.label": "Odkaz",
"upload.modal.expires.label": "Expirace",
"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.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": "D",
"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í",
@@ -274,39 +275,39 @@ export default {
"share.modal.password.title": "Heslo vyžadováno",
"share.modal.password.description": "Pro přístup k tomuto sdílení zadejte prosím heslo pro toto sdílení.",
"share.modal.password": "Heslo",
"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.",
"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": "A preview for this file type is unsupported. Please download the file to view it.",
"share.modal.error.invalid-password": "Neplatné heslo",
"share.button.download-all": "Stáhnout vše",
"share.notify.download-all-preparing": "Sdílení se připravuje. Zkuste to znovu za pár minut.",
"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": "Náhled pro tento typ souboru není podporován. Stáhněte si soubor pro jeho zobrazení.",
// 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",
"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": "Configuration",
"admin.config.category.general": "General",
"admin.config.category.share": "Share",
"admin.config.category.email": "Email",
"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": "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.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.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.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": "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.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 share email recipients",
"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.share-recipients-subject": "Share recipients subject",
@@ -316,7 +317,7 @@ export default {
"admin.config.email.reverse-share-subject": "Předmět e-mailu o zpětném sdílení",
"admin.config.email.reverse-share-subject.description": "Předmět e-mailu, který bude odeslán, když někdo vytvoří sdílení s vaším odkazem na zpětné sdílení.",
"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 reverzní sdílení. {shareUrl} bude nahrazeno jménem tvůrce a URL pro 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": "Předmět e-mailu, který bude odeslán, když uživatel požádá o obnovení hesla.",
"admin.config.email.reset-password-message": "Zpráva o obnovení hesla",
@@ -456,6 +457,7 @@ export default {
"common.text.link": "Odkaz",
"common.text.navigate-to-link": "Přejít na odkaz",
"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",
@@ -463,9 +465,9 @@ export default {
"common.error": "Chyba",
"common.error.unknown": "Došlo k neznámé chybě",
"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.exact-length": "Must be exactly {length} characters",
"common.error.invalid-number": "Must be a number",
"common.error.field-required": "This field is required"
"common.error.too-short": "Musí mít alespoň {length} znaků",
"common.error.too-long": "Musí mít maximálně {length} znaků",
"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

@@ -40,6 +40,7 @@ export default {
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
"signIn.error.invalid-username": "Invalid username",
// END /auth/signin
// /auth/signup
"signup.title": "Opret en bruger",
@@ -456,6 +457,7 @@ export default {
"common.text.link": "Link",
"common.text.navigate-to-link": "Go to the link",
"common.text.or": "eller",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "Gå tilbage",
"common.button.go-home": "Go home",
"common.notify.copied": "Linket blev kopieret til udklipsholderen",

View File

@@ -40,6 +40,7 @@ export default {
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
"signIn.error.invalid-username": "Invalid username",
// END /auth/signin
// /auth/signup
"signup.title": "Erstelle ein Konto",
@@ -417,7 +418,7 @@ export default {
"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": "Administratorengruppe",
"admin.config.ldap.admin-groups.description": "Group required for administrative access.",
// 404
"404.description": "Ups, diese Seite existiert nicht.",
@@ -456,6 +457,7 @@ export default {
"common.text.link": "Link",
"common.text.navigate-to-link": "Link öffnen",
"common.text.or": "oder",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "Zurück",
"common.button.go-home": "Zur Startseite",
"common.notify.copied": "Dein Link wurde in die Zwischenablage kopiert",

View File

@@ -34,12 +34,13 @@ export default {
"signIn.notify.totp-required.title": "Απαιτείται έλεγχος ταυτότητας δύο παραγόντων.",
"signIn.notify.totp-required.description": "Παρακαλώ εισάγετε τον κωδικό 2FA.",
"signIn.oauth.or": "Ή",
"signIn.oauth.signInWith": "Sign in with",
"signIn.oauth.signInWith": "Σύνδεση με",
"signIn.oauth.github": "GitHub",
"signIn.oauth.google": "Google",
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
"signIn.error.invalid-username": "Invalid username",
// END /auth/signin
// /auth/signup
"signup.title": "Δημιουργία λογαριασμού",
@@ -152,10 +153,10 @@ export default {
"account.reverseShares.modal.max-size.label": "Μέγιστο μέγεθος κοινοποίησης",
"account.reverseShares.modal.send-email": "Αποστολή ειδοποιήσεων με email",
"account.reverseShares.modal.send-email.description": "Στείλτε μια ειδοποίηση μέσω ηλεκτρονικού ταχυδρομείου όταν δημιουργείται ένας διαμοιρασμός με αυτόν τον σύνδεσμο ανάστροφης κοινοποίησης.",
"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 be able to customize only the name and description of the share.",
"account.reverseShares.modal.public-access": "Public access",
"account.reverseShares.modal.public-access.description": "Make the created shares with this reverse share public. If disabled, only you and the creator of the share can view it.",
"account.reverseShares.modal.simplified": "Απλή λειτουργία",
"account.reverseShares.modal.simplified.description": "Κάντε εύκολο για το άτομο που ανεβάζει το αρχείο να το μοιραστεί μαζί σας. Θα είναι σε θέση να προσαρμόσει μόνο το όνομα και την περιγραφή της κοινοποίησης.",
"account.reverseShares.modal.public-access": "Δημόσια πρόσβαση",
"account.reverseShares.modal.public-access.description": "Κάντε τις δημιουργημένες κοινοποιήσεις με αυτή την αντίστροφη κοινή χρήση δημόσιες. Εάν απενεργοποιηθεί, μόνο εσείς και ο δημιουργός της κοινής χρήσης μπορείτε να τη δείτε.",
"account.reverseShares.modal.max-use.label": "Μέγιστες χρήσεις",
"account.reverseShares.modal.max-use.description": "Ο μέγιστος αριθμός που μπορεί να χρησιμοποιηθεί αυτό το URL για τη δημιουργία ενός διαμοιρασμού.",
"account.reverseShare.never-expires": "Αυτός ο αντίστροφος διαμοιρασμός δε λήγει.",
@@ -174,7 +175,7 @@ export default {
// /admin
"admin.title": "Διαχείριση",
"admin.button.users": "Διαχείριση χρηστών",
"admin.button.shares": "Share management",
"admin.button.shares": "Διαχείριση κοινοποιήσεων",
"admin.button.config": "Διαμόρφωση",
"admin.version": "Έκδοση",
// END /admin
@@ -202,13 +203,13 @@ 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": "Λήγει στις",
"admin.shares.edit.delete.title": "Διαγραφή κοινοποίησης {id}",
"admin.shares.edit.delete.description": "Θέλετε πραγματικά να διαγράψετε αυτή τη κοινοποίηση;",
// END /admin/shares
// /upload
"upload.title": "Μεταφόρτωση",
@@ -244,9 +245,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",
@@ -259,7 +260,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.",
"upload.modal.completed.notified-reverse-share-creator": "Έχουμε ειδοποιήσει τον δημιουργό της αντίστροφης κοινής χρήσης. Μπορείτε επίσης να μοιραστείτε χειροκίνητα αυτόν τον σύνδεσμο μαζί τους μέσω άλλων μέσων.",
// END /upload
// /share/[id]
"share.title": "Διαμοιρασμός {shareId}",
@@ -269,8 +270,8 @@ 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.error.access-denied.title": "Ιδιωτική κοινοποίηση",
"share.error.access-denied.description": "Ο τρέχων λογαριασμός δεν έχει δικαίωμα πρόσβασης σε αυτήν την κοινοποίηση",
"share.modal.password.title": "Απαιτείται κωδικός",
"share.modal.password.description": "Για να αποκτήσετε πρόσβαση σε αυτή την κοινοποίηση εισάγετε τον κωδικό πρόσβασης.",
"share.modal.password": "Κωδικός πρόσβασης",
@@ -302,8 +303,8 @@ export default {
"admin.config.general.app-url.description": "Η διεύθυνση URL όπου το Pingvin Share είναι διαθέσιμο",
"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": "Επιλέξτε εικόνα",
@@ -456,6 +457,7 @@ export default {
"common.text.link": "Σύνδεσμος",
"common.text.navigate-to-link": "Μεταβείτε στο σύνδεσμο",
"common.text.or": "ή",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "Επιστροφή",
"common.button.go-home": "Μετάβαση στην αρχική",
"common.notify.copied": "Ο σύνδεσμος σας αντιγράφηκε στο πρόχειρο",

View File

@@ -50,6 +50,7 @@ export default {
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
"signIn.error.invalid-username": "Invalid username",
// END /auth/signin
@@ -631,6 +632,7 @@ export default {
"common.text.link": "Link",
"common.text.navigate-to-link": "Go to the 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",

View File

@@ -40,6 +40,7 @@ export default {
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
"signIn.error.invalid-username": "Invalid username",
// END /auth/signin
// /auth/signup
"signup.title": "Crear una cuenta",
@@ -456,6 +457,7 @@ export default {
"common.text.link": "Enlace",
"common.text.navigate-to-link": "Ir al enlace",
"common.text.or": "o",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "Volver",
"common.button.go-home": "Página de inicio",
"common.notify.copied": "Tu enlace se ha copiado al portapapeles",

View File

@@ -40,6 +40,7 @@ export default {
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
"signIn.error.invalid-username": "Invalid username",
// END /auth/signin
// /auth/signup
"signup.title": "Rekisteröidy",
@@ -456,6 +457,7 @@ export default {
"common.text.link": "Linkki",
"common.text.navigate-to-link": "Go to the 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",

View File

@@ -40,6 +40,7 @@ export default {
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
"signIn.error.invalid-username": "Invalid username",
// END /auth/signin
// /auth/signup
"signup.title": "Créer un compte",
@@ -405,19 +406,19 @@ export default {
"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": "Enabled 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 which will be used to execute the user search",
"admin.config.ldap.bind-password": "Bind password",
"admin.config.ldap.bind-password.description": "Password for the user search user",
"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.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": "Lier le DN",
"admin.config.ldap.bind-dn.description": "Utilisateur par défaut qui sera utilisé pour exécuter la recherche de l'utilisateur",
"admin.config.ldap.bind-password": "Lier le mot de passe",
"admin.config.ldap.bind-password.description": "Mot de passe pour l'utilisateur recherché",
"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": "Group required for administrative access.",
// 404
"404.description": "Désolé, mais cette page nexiste pas.",
@@ -456,6 +457,7 @@ export default {
"common.text.link": "Lien",
"common.text.navigate-to-link": "Accéder au lien",
"common.text.or": "ou",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "Précédent",
"common.button.go-home": "Accueil",
"common.notify.copied": "Votre lien a été copié dans le presse-papiers",

View File

@@ -40,6 +40,7 @@ export default {
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
"signIn.error.invalid-username": "Invalid username",
// END /auth/signin
// /auth/signup
"signup.title": "Fiók létrehozása",
@@ -456,6 +457,7 @@ export default {
"common.text.link": "Hivatkozás",
"common.text.navigate-to-link": "Ugrás a hivatkozásra",
"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",

View File

@@ -40,6 +40,7 @@ export default {
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
"signIn.error.invalid-username": "Nome utente non valido",
// END /auth/signin
// /auth/signup
"signup.title": "Crea un account",
@@ -324,7 +325,7 @@ export default {
"admin.config.email.invite-subject": "Oggetto dell'email di invito",
"admin.config.email.invite-subject.description": "Oggetto dell'email che viene inviata quando un amministratore invita un utente.",
"admin.config.email.invite-message": "Testo dell'email di invito",
"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 password of the user.",
"admin.config.email.invite-message.description": "Messaggio che viene inviato quando un amministratore invita un utente. {url} sarà sostituito con l'URL di invito, {email} con l'email e {password} con la password dell'utente.",
"admin.config.share.allow-registration": "Consenti la registrazione",
"admin.config.share.allow-registration.description": "Indica se la registrazione è autorizzata",
"admin.config.share.allow-unauthenticated-shares": "Consenti condivisioni non autenticate",
@@ -405,20 +406,20 @@ export default {
"admin.config.oauth.oidc-client-secret": "OpenID Connect Client secret",
"admin.config.oauth.oidc-client-secret.description": "Client secret dell'app OAuth OpenID Connect",
"admin.config.category.ldap": "LDAP",
"admin.config.ldap.enabled": "Enabled 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 which will be used to execute the user search",
"admin.config.ldap.bind-password": "Bind password",
"admin.config.ldap.bind-password.description": "Password for the user search user",
"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.enabled": "LDAP Abilitato",
"admin.config.ldap.enabled.description": "Usa autenticazione LDAP per l'accesso utente",
"admin.config.ldap.url": "Server Url",
"admin.config.ldap.url.description": "URL del server LDAP",
"admin.config.ldap.bind-dn": "Associa DN",
"admin.config.ldap.bind-dn.description": "Utente predefinito che verrà usato per eseguire la ricerca utente",
"admin.config.ldap.bind-password": "Associa password",
"admin.config.ldap.bind-password.description": "Password per l'utente di ricerca",
"admin.config.ldap.search-base": "Base utenti",
"admin.config.ldap.search-base.description": "Posizione di base, dove verrà eseguita la ricerca dell'utente",
"admin.config.ldap.search-query": "Interrogazione utente",
"admin.config.ldap.search-query.description": "La query utente verrà utilizzata per cercare la 'base utente' per l'utente LDAP. %username% può essere usato come segnaposto per l'input dato dall'utente.",
"admin.config.ldap.admin-groups": "Gruppo di amministrazione",
"admin.config.ldap.admin-groups.description": "Gruppo richiesto per laccesso amministrativo.",
// 404
"404.description": "Ops, questa pagina non esiste.",
"404.button.home": "Riportami a casa",
@@ -456,6 +457,7 @@ export default {
"common.text.link": "Collegamento",
"common.text.navigate-to-link": "Vai al collegamento",
"common.text.or": "o",
"common.text.redirecting": "Reindirizzamento...",
"common.button.go-back": "Torna indietro",
"common.button.go-home": "Vai alla Home Page",
"common.notify.copied": "Il tuo collegamento e' stato copiato negli appunti",

View File

@@ -40,6 +40,7 @@ export default {
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
"signIn.error.invalid-username": "Invalid username",
// END /auth/signin
// /auth/signup
"signup.title": "アカウントを作成",
@@ -456,6 +457,7 @@ export default {
"common.text.link": "リンク",
"common.text.navigate-to-link": "リンクへ移動",
"common.text.or": "または",
"common.text.redirecting": "リダイレクトしています...",
"common.button.go-back": "戻る",
"common.button.go-home": "ホームに戻る",
"common.notify.copied": "リンクをクリップボードにコピーしました",

View File

@@ -40,6 +40,7 @@ export default {
"signIn.oauth.microsoft": "마이크로소프트",
"signIn.oauth.discord": "디스코드",
"signIn.oauth.oidc": "오픈ID",
"signIn.error.invalid-username": "Invalid username",
// END /auth/signin
// /auth/signup
"signup.title": "계정 만들기",
@@ -456,6 +457,7 @@ export default {
"common.text.link": "링크",
"common.text.navigate-to-link": "링크로 이동",
"common.text.or": "또는",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "뒤로 가기",
"common.button.go-home": "첫 페이지",
"common.notify.copied": "당신의 링크가 클립보드에 복사되었습니다.",

View File

@@ -40,6 +40,7 @@ export default {
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
"signIn.error.invalid-username": "Invalid username",
// END /auth/signin
// /auth/signup
"signup.title": "Account aanmaken",
@@ -150,12 +151,12 @@ export default {
"account.reverseShares.modal.expiration.year-singular": "Jaar",
"account.reverseShares.modal.expiration.year-plural": "Jaren",
"account.reverseShares.modal.max-size.label": "Maximale share-grootte",
"account.reverseShares.modal.send-email": "Stuur e-mailnotificatie",
"account.reverseShares.modal.send-email.description": "Stuur een e-mailnotificatie wanneer er bestanden zijn gedeeld via deze omgekeerde share link.",
"account.reverseShares.modal.send-email": "Stuur e-mail notificatie",
"account.reverseShares.modal.send-email.description": "Stuur een e-mail notificatie wanneer er bestanden zijn gedeeld via deze omgekeerde 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 be able to customize only the name and description of the share.",
"account.reverseShares.modal.simplified.description": "Maak het makkelijk voor de persoon die het bestand uploadt om het met u te delen. Ze kunnen alleen de naam en beschrijving van de share aanpassen.",
"account.reverseShares.modal.public-access": "Public access",
"account.reverseShares.modal.public-access.description": "Make the created shares with this reverse share public. If disabled, only you and the creator of the share can view it.",
"account.reverseShares.modal.public-access.description": "Maak de gemaakte shares met deze reverse share openbaar. Als dit is uitgeschakeld, kunnen alleen jij en de maker van de share deze bekijken.",
"account.reverseShares.modal.max-use.label": "Maximaal gebruikte keren",
"account.reverseShares.modal.max-use.description": "Maximale keren dat deze URL gebruikt kan worden om een share aan te maken.",
"account.reverseShare.never-expires": "Deze omgekeerde share zal nooit verlopen.",
@@ -246,7 +247,7 @@ export default {
"upload.modal.expires.year-plural": "Jaren",
"upload.modal.accordion.name-and-description.title": "Naam en beschrijving",
"upload.modal.accordion.name-and-description.name.placeholder": "Naam",
"upload.modal.accordion.name-and-description.description.placeholder": "Note for the recipients of this share",
"upload.modal.accordion.name-and-description.description.placeholder": "Opmerking voor de ontvangers van deze share",
"upload.modal.accordion.email.title": "E-mail van de ontvangers",
"upload.modal.accordion.email.placeholder": "Voer e-mail ontvangers in",
"upload.modal.accordion.email.invalid-email": "Ongeldig e-mailadres",
@@ -259,7 +260,7 @@ export default {
"upload.modal.completed.never-expires": "Deze omgekeerde share zal nooit verlopen.",
"upload.modal.completed.expires-on": "Deze omgekeerde share verloopt op {expiration}.",
"upload.modal.completed.share-ready": "Share is gereed",
"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.",
"upload.modal.completed.notified-reverse-share-creator": "We hebben de maker van de reverse share op de hoogte gesteld. U kunt deze link ook handmatig met hen delen via andere middelen.",
// END /upload
// /share/[id]
"share.title": "Share {shareId}",
@@ -270,7 +271,7 @@ export default {
"share.error.not-found.title": "Share niet gevonden",
"share.error.not-found.description": "De share die u zoekt kan niet gevonden worden.",
"share.error.access-denied.title": "Private share",
"share.error.access-denied.description": "The current account does not have permission to access this share",
"share.error.access-denied.description": "Dit account heeft geen toestemming om toegang te krijgen tot deze share",
"share.modal.password.title": "Wachtwoord vereist",
"share.modal.password.description": "Vul een wachtwoord in om toegang te krijgen tot deze share.",
"share.modal.password": "Wachtwoord",
@@ -302,8 +303,8 @@ export default {
"admin.config.general.app-url.description": "De URL waar Pingvin Share bereikbaar is",
"admin.config.general.show-home-page": "Toon startpagina",
"admin.config.general.show-home-page.description": "Toon of verberg de home pagina",
"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": "Sessieduur",
"admin.config.general.session-duration.description": "Tijd in uren waarna een gebruiker zich opnieuw moet aanmelden (standaard: 3 maanden).",
"admin.config.general.logo": "Logo",
"admin.config.general.logo.description": "Verander uw logo door een nieuwe afbeelding te uploaden. De afbeelding moet PNG zijn en het formaat moet 1:1 hebben.",
"admin.config.general.logo.placeholder": "Afbeelding kiezen",
@@ -324,7 +325,7 @@ export default {
"admin.config.email.invite-subject": "Onderwerp voor uitnodiging",
"admin.config.email.invite-subject.description": "Onderwerp van de e-mail die wordt verzonden wanneer een beheerder een gebruiker uitnodigt.",
"admin.config.email.invite-message": "Bericht voor uitnodiging",
"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 password of the user.",
"admin.config.email.invite-message.description": "Bericht dat wordt verzonden wanneer een beheerder een gebruiker uitnodigt. {url} wordt vervangen door de uitnodigings-URL, {email} door het e-mailadres en {password} door het wachtwoord van de gebruiker.",
"admin.config.share.allow-registration": "Sta registratie toe",
"admin.config.share.allow-registration.description": "Of registratie is toegestaan",
"admin.config.share.allow-unauthenticated-shares": "Ongeverifieerde shares toestaan",
@@ -336,7 +337,7 @@ export default {
"admin.config.share.zip-compression-level": "Zip compressie niveau",
"admin.config.share.zip-compression-level.description": "Pas het niveau aan voor evenwicht tussen bestandsgrootte en compressie snelheid. Geldige waarden variëren van 0 tot 9, waarbij 0 geen compressie is en 9 de maximale compressie is. ",
"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": "Pas de chunkgrootte (in bytes) voor uw uploads aan om efficiëntie en betrouwbaarheid in evenwicht te brengen op basis van uw internetverbinding. Kleinere chunks kunnen de slagingspercentages voor onstabiele verbindingen verbeteren, terwijl grotere chunks uploads voor stabiele verbindingen versnellen.",
"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": "Inschakelen",
@@ -351,15 +352,15 @@ export default {
"admin.config.smtp.username.description": "Gebruikersnaam van de SMTP-server",
"admin.config.smtp.password": "Wachtwoord",
"admin.config.smtp.password.description": "Wachtwoord van de SMTP-server",
"admin.config.smtp.button.test": "Teste-mail verzenden",
"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.smtp.button.test": "Test e-mail verzenden",
"admin.config.smtp.allow-unauthorized-certificates": "Vertrouw ongeautoriseerde SMTP-servercertificaten",
"admin.config.smtp.allow-unauthorized-certificates.description": "Zet dit alleen aan als je de self signed certificates vertrouwt.",
"admin.config.oauth.allow-registration": "Sta registratie toe",
"admin.config.oauth.allow-registration.description": "Gebruikers toestaan zich te registreren via sociale login",
"admin.config.oauth.ignore-totp": "TOTP negeren",
"admin.config.oauth.ignore-totp.description": "TOTP negeren wanneer gebruiker sociale login gebruikt",
"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.disable-password": "Zet password login uit",
"admin.config.oauth.disable-password.description": "Of password login moet worden uitgeschakeld\nZorg ervoor dat een OAuth-provider correct is geconfigureerd voordat u deze configuratie activeert om te voorkomen dat u wordt buitengesloten.",
"admin.config.oauth.github-enabled": "GitHub",
"admin.config.oauth.github-enabled.description": "Ofdat GitHub login is ingeschakeld",
"admin.config.oauth.github-client-id": "GitHub Client ID",
@@ -395,30 +396,30 @@ export default {
"admin.config.oauth.oidc-username-claim": "OpenID Connect username claim",
"admin.config.oauth.oidc-username-claim.description": "Gebruikersnaam claim in OpenID Connect-ID-token. Laat het leeg als u niet weet wat deze configuratie 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-role-path.description": "Moet een geldig JMES-pad zijn dat verwijst naar een reeks rollen. " + "Het beheren van toegangsrechten met behulp van OpenID Connect-rollen wordt alleen aanbevolen als er geen andere identiteitsprovider is geconfigureerd en wachtwoordaanmelding is uitgeschakeld. " + "Laat dit leeg als u niet weet wat deze configuratie is.",
"admin.config.oauth.oidc-role-general-access": "OpenID Connect role voor algemene toegang",
"admin.config.oauth.oidc-role-general-access.description": "Rol vereist voor algemene toegang. Moet aanwezig zijn in de rollen van een gebruiker om in te loggen. " + "Laat het leeg als u niet weet wat deze configuratie is.",
"admin.config.oauth.oidc-role-admin-access": "OpenID Connect role voor admin toegang",
"admin.config.oauth.oidc-role-admin-access.description": "Rol vereist voor administratieve toegang. Moet aanwezig zijn in de rollen van een gebruiker om toegang te krijgen tot het admin-paneel. " + "Laat dit leeg als u niet weet wat deze configuratie is.",
"admin.config.oauth.oidc-client-id": "Client-ID OpenID Connect",
"admin.config.oauth.oidc-client-id.description": "Client-ID van de OpenID Connect OAuth app",
"admin.config.oauth.oidc-client-secret": "OpenID Connect client secret",
"admin.config.oauth.oidc-client-secret.description": "Client secret van de OpenID Connect OAuth app",
"admin.config.category.ldap": "LDAP",
"admin.config.ldap.enabled": "Enabled LDAP",
"admin.config.ldap.enabled.description": "Use LDAP authentication for user login",
"admin.config.ldap.enabled.description": "Gebruik LDAP authentication voor gebruiker login",
"admin.config.ldap.url": "Server URL",
"admin.config.ldap.url.description": "URL of the LDAP server",
"admin.config.ldap.url.description": "URL van de LDAP server",
"admin.config.ldap.bind-dn": "Bind DN",
"admin.config.ldap.bind-dn.description": "Default user which will be used to execute the user search",
"admin.config.ldap.bind-dn.description": "Standaardgebruiker die zal worden gebruikt om de gebruikerszoekopdracht uit te voeren",
"admin.config.ldap.bind-password": "Bind password",
"admin.config.ldap.bind-password.description": "Password for the user search user",
"admin.config.ldap.bind-password.description": "Wachtwoord voor de user search gebruiker",
"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.admin-groups": "Admin groep",
"admin.config.ldap.admin-groups.description": "Groep vereist voor administratieve toegang.",
// 404
"404.description": "Oeps, deze pagina bestaat niet.",
"404.button.home": "Breng me terug naar huis",
@@ -456,6 +457,7 @@ export default {
"common.text.link": "Koppeling",
"common.text.navigate-to-link": "Ga naar de koppeling",
"common.text.or": "of",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "Ga terug",
"common.button.go-home": "Go home",
"common.notify.copied": "Uw link is gekopieerd naar het klembord",
@@ -468,4 +470,4 @@ export default {
"common.error.exact-length": "Moet precies {length} tekens bevatten",
"common.error.invalid-number": "Moet een getal zijn",
"common.error.field-required": "Dit veld is verplicht"
};
};

View File

@@ -34,12 +34,13 @@ export default {
"signIn.notify.totp-required.title": "Wymagane jest uwierzytelnianie dwuetapowe",
"signIn.notify.totp-required.description": "Wprowadź kod uwierzytelniania dwuetapowego",
"signIn.oauth.or": "LUB",
"signIn.oauth.signInWith": "Sign in with",
"signIn.oauth.signInWith": "Zaloguj się przez",
"signIn.oauth.github": "GitHub",
"signIn.oauth.google": "Google",
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
"signIn.error.invalid-username": "Invalid username",
// END /auth/signin
// /auth/signup
"signup.title": "Utwórz konto",
@@ -152,10 +153,10 @@ export default {
"account.reverseShares.modal.max-size.label": "Maksymalny rozmiar udziału",
"account.reverseShares.modal.send-email": "Wysyłanie powiadomienia e-mail",
"account.reverseShares.modal.send-email.description": "Wyślij powiadomienie e-mail, gdy udostępnianie zostanie utworzone za pomocą linku udostępniania odwrotnego.",
"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 be able to customize only the name and description of the share.",
"account.reverseShares.modal.public-access": "Public access",
"account.reverseShares.modal.public-access.description": "Make the created shares with this reverse share public. If disabled, only you and the creator of the share can view it.",
"account.reverseShares.modal.simplified": "Tryb uproszczony",
"account.reverseShares.modal.simplified.description": "Ułatwia wysyłanie pliku do Ciebie. Osoba będzie mogła dostosować nazwę i opis udziałów.",
"account.reverseShares.modal.public-access": "Dostęp publiczny",
"account.reverseShares.modal.public-access.description": "Ustaw to udostępnienie odwrotne jako publiczne. Gdy wyłączone, tylko ty i twórca udostępnienia może je zobaczyć.",
"account.reverseShares.modal.max-use.label": "Limit użyć",
"account.reverseShares.modal.max-use.description": "Maksymalna ilość razy, kiedy ten adres URL może być użyty do utworzenia udostępniania.",
"account.reverseShare.never-expires": "To udostępnienie odwrotne nigdy nie wygasa.",
@@ -174,7 +175,7 @@ export default {
// /admin
"admin.title": "Administracja",
"admin.button.users": "Zarządzanie użytkownikami",
"admin.button.shares": "Share management",
"admin.button.shares": "Zarządzanie udostępnieniami",
"admin.button.config": "Konfiguracja",
"admin.version": "Wersja",
// END /admin
@@ -202,13 +203,13 @@ export default {
"admin.users.modal.create.admin.description": "Jeśli zaznaczone, użytkownik będzie miał dostęp do panelu administratora.",
// 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": "Zarządzanie udostępnieniami",
"admin.shares.table.id": "ID Udostępnienia",
"admin.shares.table.username": "Twórca",
"admin.shares.table.visitors": "Odwiedzający",
"admin.shares.table.expires": "Wygasa",
"admin.shares.edit.delete.title": "Usuń udostępnienie {id}",
"admin.shares.edit.delete.description": "Czy na pewno chcesz usunąć to udostępnienie?",
// END /admin/shares
// /upload
"upload.title": "Prześlij",
@@ -244,9 +245,9 @@ export default {
"upload.modal.expires.month-plural": "Miesiące/ęcy",
"upload.modal.expires.year-singular": "Rok",
"upload.modal.expires.year-plural": "Lat/a",
"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": "Nazwa i opis",
"upload.modal.accordion.name-and-description.name.placeholder": "Nazwa",
"upload.modal.accordion.name-and-description.description.placeholder": "Notatka dla odbiorców",
"upload.modal.accordion.email.title": "Odbiorcy wiadomości e-mail",
"upload.modal.accordion.email.placeholder": "Wprowadź adresatów wiadomości e-mail",
"upload.modal.accordion.email.invalid-email": "Adres e-mail jest nieprawidłowy",
@@ -259,7 +260,7 @@ export default {
"upload.modal.completed.never-expires": "To udostępnienie nigdy nie wygaśnie.",
"upload.modal.completed.expires-on": "To udostępnienie wygaśnie dnia {expiration}.",
"upload.modal.completed.share-ready": "Udostępnianie gotowe",
"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.",
"upload.modal.completed.notified-reverse-share-creator": "Powiadomiliśmy twórcę odwrotnego udostępnienia. Możesz również ręcznie udostępnić ten link w inny sposób.",
// END /upload
// /share/[id]
"share.title": "Udostępnij {shareId}",
@@ -269,8 +270,8 @@ export default {
"share.error.removed.title": "Udostępnianie usunięte",
"share.error.not-found.title": "Nie znaleziono udziału",
"share.error.not-found.description": "Udział, który szukasz, nie istnieje.",
"share.error.access-denied.title": "Private share",
"share.error.access-denied.description": "The current account does not have permission to access this share",
"share.error.access-denied.title": "Prywatne udostępnienie",
"share.error.access-denied.description": "Bieżące konto nie ma uprawnień dostępu do tego udostępnienia",
"share.modal.password.title": "Wymagane hasło",
"share.modal.password.description": "Aby uzyskać dostęp do tego udziału, wprowadź hasło.",
"share.modal.password": "Hasło",
@@ -302,8 +303,8 @@ export default {
"admin.config.general.app-url.description": "Pod którym adresem URL Pingvin Share jest dostępny",
"admin.config.general.show-home-page": "Pokaż stronę główną",
"admin.config.general.show-home-page.description": "Czy wyświetlać stronę główną",
"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": "Czas trwania sesji",
"admin.config.general.session-duration.description": "Czas w godzinach, po którym użytkownik musi zalogować się ponownie (domyślnie: 3 miesiące).",
"admin.config.general.logo": "Logo",
"admin.config.general.logo.description": "Zmień logo, przesyłając nowy obraz. Obraz musi być plikiem PNG i mieć proporcje 1:1.",
"admin.config.general.logo.placeholder": "Wybierz obraz",
@@ -324,7 +325,7 @@ export default {
"admin.config.email.invite-subject": "Temat zaproszenia",
"admin.config.email.invite-subject.description": "Temat wiadomości e-mail, która zostanie wysłana, gdy administrator zaprasza użytkownika.",
"admin.config.email.invite-message": "Wiadomość zaproszenia",
"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 password of the user.",
"admin.config.email.invite-message.description": "Wiadomość, która zostanie wysłana, gdy administrator zaprasza użytkownika. {url} zostanie zastąpiony adresem URL zaproszenia, {email} adresem e-mail a {password} hasłem użytkownika.",
"admin.config.share.allow-registration": "Zezwól na rejestrację",
"admin.config.share.allow-registration.description": "Czy dozwolona jest rejestracja",
"admin.config.share.allow-unauthenticated-shares": "Zezwalaj na nieuwierzytelnione udostępnianie",
@@ -335,8 +336,8 @@ export default {
"admin.config.share.max-size.description": "Maksymalny rozmiar udziału w bajtach",
"admin.config.share.zip-compression-level": "Poziom kompresji Zip",
"admin.config.share.zip-compression-level.description": "Dostosuj poziom do równowagi między rozmiarem pliku a szybkością kompresji. Prawidłowe wartości mieszczą się w zakresie od 0 do 9, przy czym 0 to brak kompresji a 9 maksymalną kompresją. ",
"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": "Rozmiar fragmentu",
"admin.config.share.chunk-size.description": "Dostosuj rozmiar fragmentu (w bajtach) dla przesyłanych plików w celu zrównoważenia wydajności i niezawodności zgodnie z Twoim połączeniem internetowym. Mniejsze fragmenty mogą zwiększyć szanse na powodzenie dla niestabilnych połączeń, podczas gdy większe fragmenty przyspieszają wysyłanie dla stabilnych połączeń.",
"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": "Włączony",
@@ -456,6 +457,7 @@ export default {
"common.text.link": "Link",
"common.text.navigate-to-link": "Przejdź do linku",
"common.text.or": "lub",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "Wróć",
"common.button.go-home": "Wróć do ekranu głównego",
"common.notify.copied": "Link został skopiowany do schowka",

View File

@@ -40,6 +40,7 @@ export default {
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
"signIn.error.invalid-username": "Usuário inválido",
// END /auth/signin
// /auth/signup
"signup.title": "Criar uma conta",
@@ -324,7 +325,7 @@ export default {
"admin.config.email.invite-subject": "Assunto do convite",
"admin.config.email.invite-subject.description": "Assunto do e-mail enviado quando um administrador convida um usuário.",
"admin.config.email.invite-message": "Mensagem de convite",
"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 password of the user.",
"admin.config.email.invite-message.description": "Mensagem que é enviada quando um administrador convida um usuário. {url} será substituído pelo URL de convite, {email} com o e-mail e {password} com a senha do usuário.",
"admin.config.share.allow-registration": "Permitir novos registos",
"admin.config.share.allow-registration.description": "Se o registro é permitido",
"admin.config.share.allow-unauthenticated-shares": "Permitir compartilhamentos sem autenticação",
@@ -456,6 +457,7 @@ export default {
"common.text.link": "Link",
"common.text.navigate-to-link": "Ir para o link",
"common.text.or": "ou",
"common.text.redirecting": "Redirecionando...",
"common.button.go-back": "Voltar",
"common.button.go-home": "Voltar para o Início",
"common.notify.copied": "O seu link foi copiado para a área de transferência",

View File

@@ -40,6 +40,7 @@ export default {
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
"signIn.error.invalid-username": "Invalid username",
// END /auth/signin
// /auth/signup
"signup.title": "Создать аккаунт",
@@ -456,6 +457,7 @@ export default {
"common.text.link": "Ссылка",
"common.text.navigate-to-link": "Go to the link",
"common.text.or": "или",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "Назад",
"common.button.go-home": "Домой",
"common.notify.copied": "Ваша ссылка скопирована в буфер обмена",

View File

@@ -40,6 +40,7 @@ export default {
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
"signIn.error.invalid-username": "Invalid username",
// END /auth/signin
// /auth/signup
"signup.title": "Ustvarite račun",
@@ -456,6 +457,7 @@ export default {
"common.text.link": "Poveži",
"common.text.navigate-to-link": "Pojdi na povezavo",
"common.text.or": "ali",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "Nazaj",
"common.button.go-home": "Pojdi domov",
"common.notify.copied": "Povezava je bila kopirana v odložišče",

View File

@@ -34,12 +34,13 @@ export default {
"signIn.notify.totp-required.title": "Потребна је двофакторска аутентификација",
"signIn.notify.totp-required.description": "Унесите свој двофакторски код за аутентификацију",
"signIn.oauth.or": "Или",
"signIn.oauth.signInWith": "Sign in with",
"signIn.oauth.signInWith": "Пријавите се са",
"signIn.oauth.github": "GitHub",
"signIn.oauth.google": "Google",
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
"signIn.error.invalid-username": "Неважеће корисничко име",
// END /auth/signin
// /auth/signup
"signup.title": "Направи налог",
@@ -58,7 +59,7 @@ export default {
// /auth/reset-password
"resetPassword.title": "Заборавили сте лозинку?",
"resetPassword.description": "Унесите своју е-пошту да бисте ресетовали лозинку.",
"resetPassword.notify.success": "A message with a link to reset your password has been sent if the email exists.",
"resetPassword.notify.success": "Порука са везом за ресетовање ваше лозинке је послата ако имејл постоји.",
"resetPassword.button.back": "Назад на страницу за пријаву",
"resetPassword.text.resetPassword": "Обнови лозинку",
"resetPassword.text.enterNewPassword": "Унесите вашу нову лозинку",
@@ -152,10 +153,10 @@ export default {
"account.reverseShares.modal.max-size.label": "Максимална величина дељења",
"account.reverseShares.modal.send-email": "Пошаљите обавештење путем е-поште",
"account.reverseShares.modal.send-email.description": "Пошаљите обавештење е-поштом када се креира дељење помоћу ове обрнуте везе за дељење.",
"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 be able to customize only the name and description of the share.",
"account.reverseShares.modal.public-access": "Public access",
"account.reverseShares.modal.public-access.description": "Make the created shares with this reverse share public. If disabled, only you and the creator of the share can view it.",
"account.reverseShares.modal.simplified": "Једноставан режим",
"account.reverseShares.modal.simplified.description": "Олакшајте особи која отпрема датотеку да је подели са вама. Они ће моћи да прилагоде само име и опис дељења.",
"account.reverseShares.modal.public-access": "Јавни приступ",
"account.reverseShares.modal.public-access.description": "Учините креирана дељења са овим обрнутим дељењем јавном. Ако је онемогућено, само ви и креатор дељења можете да га видите.",
"account.reverseShares.modal.max-use.label": "Максималан број коришћења",
"account.reverseShares.modal.max-use.description": "Максималан број пута који овај URL може да се користи за прављење дељења.",
"account.reverseShare.never-expires": "Ово обрнуто дељење никада неће истећи.",
@@ -174,7 +175,7 @@ export default {
// /admin
"admin.title": "Администрација",
"admin.button.users": "Управљање корисницима",
"admin.button.shares": "Share management",
"admin.button.shares": "Управљање deljenjima",
"admin.button.config": "Конфигурација",
"admin.version": "Верзија",
// END /admin
@@ -456,6 +457,7 @@ export default {
"common.text.link": "Линк",
"common.text.navigate-to-link": "Go to the link",
"common.text.or": "или",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "Иди назад",
"common.button.go-home": "Go home",
"common.notify.copied": "Ваша веза је копирана у clipboard",

View File

@@ -40,6 +40,7 @@ export default {
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
"signIn.error.invalid-username": "Invalid username",
// END /auth/signin
// /auth/signup
"signup.title": "Skapa ett konto",
@@ -456,6 +457,7 @@ export default {
"common.text.link": "Länk",
"common.text.navigate-to-link": "Gå till länken",
"common.text.or": "eller",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "Gå tillbaka",
"common.button.go-home": "Gå hem",
"common.notify.copied": "Din länk har kopierats till urklipp",

View File

@@ -40,6 +40,7 @@ export default {
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
"signIn.error.invalid-username": "Invalid username",
// END /auth/signin
// /auth/signup
"signup.title": "สมัครบัญชี",
@@ -456,6 +457,7 @@ export default {
"common.text.link": "ลิงค์",
"common.text.navigate-to-link": "Go to the link",
"common.text.or": "หรือ",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "ย้อนกลับ",
"common.button.go-home": "Go home",
"common.notify.copied": "คัดลอกไปยังคลิปบอร์ดแล้ว",

View File

@@ -40,6 +40,7 @@ export default {
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
"signIn.error.invalid-username": "Invalid username",
// END /auth/signin
// /auth/signup
"signup.title": "Bir hesap oluştur",
@@ -456,6 +457,7 @@ export default {
"common.text.link": "Bağlantı",
"common.text.navigate-to-link": "Bağlantıya git",
"common.text.or": "ya da",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "Geri dön",
"common.button.go-home": "Ana sayfaya dön",
"common.notify.copied": "Bağlantınız kopyalandı",

View File

@@ -40,6 +40,7 @@ export default {
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
"signIn.error.invalid-username": "Invalid username",
// END /auth/signin
// /auth/signup
"signup.title": "Створити акаунт",
@@ -456,6 +457,7 @@ export default {
"common.text.link": "Посилання",
"common.text.navigate-to-link": "Перейти до посилання",
"common.text.or": "або",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "Назад",
"common.button.go-home": "Перейти додому",
"common.notify.copied": "Ваше посилання скопійовано в буфер обміну",

View File

@@ -40,6 +40,7 @@ export default {
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
"signIn.error.invalid-username": "Invalid username",
// END /auth/signin
// /auth/signup
"signup.title": "Tạo tài khoản",
@@ -60,14 +61,14 @@ export default {
"resetPassword.description": "Nhập email để khôi phục mật khẩu.",
"resetPassword.notify.success": "Email khôi phục mật khẩu đã được gửi nếu như địa chỉ email này tồn tại.",
"resetPassword.button.back": "Quay lại trang đăng nhập",
"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.",
"resetPassword.text.resetPassword": "Đặt lại mật khẩu",
"resetPassword.text.enterNewPassword": "Mật khẩu mới",
"resetPassword.input.password": "Mật khẩu mới",
"resetPassword.notify.passwordReset": "Mật khẩu đã được thay đổi!",
// /account
"account.title": "Tài khoản",
"account.card.info.title": "Thông tin tài khoản",
"account.card.info.username": "Username",
"account.card.info.username": "Tên đăng nhập",
"account.card.info.email": "Email",
"account.notify.info.success": "Cập nhật tài khoản thành công",
"account.card.password.title": "Mật khẩu",
@@ -94,10 +95,10 @@ export default {
"account.modal.totp.title": "Bật 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",
"account.modal.totp.enterManually": "Nhập thủ công",
"account.modal.totp.code": "",
"common.button.clickToCopy": "Nhấn để sao chép",
"account.modal.totp.verify": "Verify",
"account.modal.totp.verify": "Xác thực",
"account.notify.totp.disable": "Tắt TOTP thành công",
"account.notify.totp.enable": "Bật TOTP thành công",
"account.card.language.title": "Ngôn ngữ",
@@ -185,17 +186,17 @@ export default {
"admin.users.table.admin": "Admin",
"admin.users.edit.update.title": "Update 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.update.change-password.title": "Đổi mật khẩu",
"admin.users.edit.update.change-password.field": "Mật khẩu mới",
"admin.users.edit.update.change-password.button": "Đổi mật khẩu",
"admin.users.edit.update.notify.password.success": "Thay đổi mật khẩu thành công",
"admin.users.edit.delete.title": "Xóa tài khoản {username}",
"admin.users.edit.delete.description": "Do you really want to delete this user and all his 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.password": "Mật khẩu",
"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",
@@ -204,14 +205,14 @@ export default {
// /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.table.username": "Người tạo",
"admin.shares.table.visitors": "Lượng truy cập",
"admin.shares.table.expires": "Hết hạn vào",
"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.title": "Tải lên",
"upload.notify.generic-error": "An error occurred while finishing your share.",
"upload.notify.count-failed": "{count} files failed to upload. Trying again.",
// Dropzone.tsx
@@ -219,13 +220,13 @@ export default {
"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.notify.file-too-big": "Your files exceed the maximum share size of {maxSize}.",
// FileList.tsx
"upload.filelist.name": "Name",
"upload.filelist.size": "Size",
"upload.filelist.name": "Tên",
"upload.filelist.size": "Kích thước",
// 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.link.error.invalid": "Chỉ được phép sử dụng chữ, số, dấu gạch dưới và dấu gạch ngang",
"upload.modal.link.error.taken": "Liên kết đã được sử dụng",
"upload.modal.not-signed-in": "Bạn chưa đăng nhập",
"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": "Never Expires",
@@ -244,17 +245,17 @@ export default {
"upload.modal.expires.month-plural": "Tháng",
"upload.modal.expires.year-singular": "Năm",
"upload.modal.expires.year-plural": "Năm",
"upload.modal.accordion.name-and-description.title": "Name and description",
"upload.modal.accordion.name-and-description.title": "Tên và miêu tả",
"upload.modal.accordion.name-and-description.name.placeholder": "Tên",
"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",
"upload.modal.accordion.email.invalid-email": "Địa chỉ email không hợp lệ",
"upload.modal.accordion.security.title": "Tùy chọn bảo mật",
"upload.modal.accordion.security.password.label": "Bảo vệ bằng mật khẩu",
"upload.modal.accordion.security.password.placeholder": "Không có mật khẩu",
"upload.modal.accordion.security.max-views.label": "Lượt xem tối đa",
"upload.modal.accordion.security.max-views.placeholder": "Không giới hạn",
// showCompletedUploadModal.tsx
"upload.modal.completed.never-expires": "This share will never expire.",
"upload.modal.completed.expires-on": "This share will expire on {expiration}.",
@@ -271,16 +272,16 @@ export default {
"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.title": "Cần mật khẩu để mở khoá",
"share.modal.password.description": "To access this share please enter the password for the share.",
"share.modal.password": "Mật khẩu",
"share.modal.error.invalid-password": "Mật khẩu không hợp lệ",
"share.button.download-all": "Tải xuống tất cả",
"share.notify.download-all-preparing": "The share is preparing. 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.table.name": "Tên",
"share.table.size": "Kích thước",
"share.modal.file-preview.error.not-supported.title": "Xem trước không được hỗ trợ",
"share.modal.file-preview.error.not-supported.description": "A preview for this file type is unsupported. Please download the file to view it.",
// END /share/[id]
// /share/[id]/edit
@@ -291,7 +292,7 @@ export default {
// END /share/[id]/edit
// /admin/config
"admin.config.title": "Configuration",
"admin.config.category.general": "General",
"admin.config.category.general": "Tổng quan",
"admin.config.category.share": "Share",
"admin.config.category.email": "Email",
"admin.config.category.smtp": "SMTP",
@@ -349,9 +350,9 @@ export default {
"admin.config.smtp.email.description": "Email address which the emails get sent from",
"admin.config.smtp.username": "Username",
"admin.config.smtp.username.description": "Username of the SMTP server",
"admin.config.smtp.password": "Password",
"admin.config.smtp.password": "Mật khẩu",
"admin.config.smtp.password.description": "Password of the SMTP server",
"admin.config.smtp.button.test": "Send test email",
"admin.config.smtp.button.test": "Gửi email kiểm tra",
"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",
@@ -408,7 +409,7 @@ export default {
"admin.config.ldap.enabled": "Enabled 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.url.description": "Địa chỉ máy chủ LDAP",
"admin.config.ldap.bind-dn": "Bind DN",
"admin.config.ldap.bind-dn.description": "Default user which will be used to execute the user search",
"admin.config.ldap.bind-password": "Bind password",
@@ -425,11 +426,11 @@ export default {
// error
"error.title": "Lỗi",
"error.description": "Oops!",
"error.button.back": "Go back",
"error.msg.default": "Something went wrong.",
"error.button.back": "Quay lại",
"error.msg.default": "Đã xảy ra lỗi.",
"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.invalid_token": "Lỗi nội bộ",
"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.",
@@ -456,6 +457,7 @@ export default {
"common.text.link": "Link",
"common.text.navigate-to-link": "Go to the link",
"common.text.or": "hoặc",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "Quay lại",
"common.button.go-home": "Về trang chủ",
"common.notify.copied": "Đã sao chép liên kết vào bộ nhớ",
@@ -465,7 +467,7 @@ 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.exact-length": "Must be exactly {length} characters",
"common.error.exact-length": "Bằng {length} kí tự",
"common.error.invalid-number": "Phải là số",
"common.error.field-required": "Trường bắt buộc"
};

View File

@@ -40,6 +40,7 @@ export default {
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
"signIn.error.invalid-username": "Invalid username",
// END /auth/signin
// /auth/signup
"signup.title": "创建账户",
@@ -456,6 +457,7 @@ export default {
"common.text.link": "链接",
"common.text.navigate-to-link": "访问链接",
"common.text.or": "或",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "返回",
"common.button.go-home": "返回主页",
"common.notify.copied": "已复制到剪贴板",

View File

@@ -40,6 +40,7 @@ export default {
"signIn.oauth.microsoft": "Microsoft",
"signIn.oauth.discord": "Discord",
"signIn.oauth.oidc": "OpenID",
"signIn.error.invalid-username": "Invalid username",
// END /auth/signin
// /auth/signup
"signup.title": "建立帳號",
@@ -456,6 +457,7 @@ export default {
"common.text.link": "聯結",
"common.text.navigate-to-link": "Go to the link",
"common.text.or": "或",
"common.text.redirecting": "Redirecting...",
"common.button.go-back": "返回",
"common.button.go-home": "Go home",
"common.notify.copied": "已複製到剪貼簿",

View File

@@ -1,4 +1,4 @@
import jwtDecode from "jwt-decode";
import { jwtDecode } from "jwt-decode";
import { NextRequest, NextResponse } from "next/server";
import configService from "./services/config.service";

View File

@@ -14,7 +14,7 @@ import {
import { useForm, yupResolver } from "@mantine/form";
import { useModals } from "@mantine/modals";
import { useEffect, useState } from "react";
import { Tb2Fa } from "react-icons/tb";
import { TbAuth2Fa } from "react-icons/tb";
import { FormattedMessage } from "react-intl";
import * as yup from "yup";
import Meta from "../../components/Meta";
@@ -293,7 +293,7 @@ const Account = () => {
<Tabs defaultValue="totp">
<Tabs.List>
<Tabs.Tab value="totp" icon={<Tb2Fa size={14} />}>
<Tabs.Tab value="totp" icon={<TbAuth2Fa size={14} />}>
TOTP
</Tabs.Tab>
</Tabs.List>

View File

@@ -162,9 +162,7 @@ export default function Home() {
size="md"
className={classes.control}
>
<FormattedMessage
id="home.button.start"
/>
<FormattedMessage id="home.button.start" />
</Button>
<Button
component={Link}
@@ -186,4 +184,4 @@ export default function Home() {
</Container>
</>
);
}
}

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "pingvin-share",
"version": "1.0.2",
"version": "1.1.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "pingvin-share",
"version": "1.0.2",
"version": "1.1.2",
"devDependencies": {
"conventional-changelog-cli": "^3.0.0"
}

View File

@@ -1,12 +1,12 @@
{
"name": "pingvin-share",
"version": "1.0.2",
"version": "1.1.2",
"scripts": {
"format": "cd frontend && npm run format && cd ../backend && npm run format",
"lint": "cd frontend && npm run lint && cd ../backend && npm run lint",
"version": "conventional-changelog -p conventionalcommits -i CHANGELOG.md -s && git add CHANGELOG.md",
"release:patch": "cd backend && npm version patch --commit-hooks false && cd ../frontend && npm version patch --commit-hooks false && cd .. && git add . && npm version patch --force -m 'release: %s' && git push && git push --tags",
"release:minor": "cd backend && npm version minor --commit-hooks false && cd ../frontend && npm version minor --commit-hooks false && cd .. && git add . && npm version major --force -m 'release: %s' && git push && git push --tags",
"release:minor": "cd backend && npm version minor --commit-hooks false && cd ../frontend && npm version minor --commit-hooks false && cd .. && git add . && npm version minor --force -m 'release: %s' && git push && git push --tags",
"deploy:dev": "docker buildx build --push --tag stonith404/pingvin-share:development --platform linux/amd64,linux/arm64 ."
},
"devDependencies": {

7
reverse-proxy/Caddyfile Normal file
View File

@@ -0,0 +1,7 @@
:3000 {
# Reverse proxy for /api
reverse_proxy /api/* http://localhost:8080
# Reverse proxy for all other requests
reverse_proxy http://localhost:3333
}

View File

@@ -0,0 +1,14 @@
:3000 {
reverse_proxy /* http://localhost:3333 {
trusted_proxies 0.0.0.0/0
}
reverse_proxy /api/* http://localhost:8080 {
trusted_proxies 0.0.0.0/0
}
log {
output file /var/log/caddy/access.log
level WARN
}
}

View File

@@ -4,10 +4,17 @@
cp -rn /tmp/img/* /opt/app/frontend/public/img
# Start Caddy
caddy start --config /etc/caddy/Caddyfile &
if [ "$TRUST_PROXY" = "true" ]; then
caddy start --config /etc/caddy/Caddyfile.trust-proxy &
else
caddy start --config /etc/caddy/Caddyfile &
fi
# Run the frontend server
PORT=3333 HOSTNAME=0.0.0.0 node frontend/server.js &
# Run the backend server
cd backend && npm run prod
# Wait for all processes to finish
wait -n