Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d1656688e | ||
|
|
e53a25750d | ||
|
|
ffcd857d9a | ||
|
|
48a6ceb3b4 | ||
|
|
84833e5f91 | ||
|
|
1864951bdb | ||
|
|
427e99c7b1 | ||
|
|
ccc783ab6a | ||
|
|
853f217bf1 | ||
|
|
af047c0bc1 | ||
|
|
8ddee32664 | ||
|
|
e7128dcfef | ||
|
|
56d8517073 | ||
|
|
df4341163a | ||
|
|
81547ba1e0 | ||
|
|
a790ac73fd | ||
|
|
3fd689a760 | ||
|
|
6a8ac1aa65 |
3
.github/workflows/backend-system-tests.yml
vendored
3
.github/workflows/backend-system-tests.yml
vendored
@@ -10,8 +10,9 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
system-tests:
|
system-tests:
|
||||||
|
timeout-minutes: 15
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: node:18
|
container: node:22
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
|
|||||||
1
.github/workflows/build-docker-image.yml
vendored
1
.github/workflows/build-docker-image.yml
vendored
@@ -6,6 +6,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
timeout-minutes: 60
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: checkout code
|
- name: checkout code
|
||||||
|
|||||||
16
CHANGELOG.md
16
CHANGELOG.md
@@ -1,3 +1,19 @@
|
|||||||
|
## [1.12.0](https://github.com/stonith404/pingvin-share/compare/v1.11.1...v1.12.0) (2025-05-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **s3:** stream s3 content over a zip file ([#822](https://github.com/stonith404/pingvin-share/issues/822)) ([ccc783a](https://github.com/stonith404/pingvin-share/commit/ccc783ab6a00841a7041c454e77afb472d76999e))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* disable HTML rendering in Markdown preview ([427e99c](https://github.com/stonith404/pingvin-share/commit/427e99c7b1d00ff6ed7b5fd879d8cf0f0d49281a))
|
||||||
|
* health check for containers with reverse proxy disabled ([#816](https://github.com/stonith404/pingvin-share/issues/816)) ([a790ac7](https://github.com/stonith404/pingvin-share/commit/a790ac73fd42d266a957e09a05b1894199605f6a)), closes [#809](https://github.com/stonith404/pingvin-share/issues/809)
|
||||||
|
* OIDC configuration from YAML configuration file doesn't get loaded ([48a6ceb](https://github.com/stonith404/pingvin-share/commit/48a6ceb3b4b4dfc0407dc6f9ee2e07cca1829cef))
|
||||||
|
* spelling mistake and add clarity in email template ([#824](https://github.com/stonith404/pingvin-share/issues/824)) ([af047c0](https://github.com/stonith404/pingvin-share/commit/af047c0bc152a955b3ab135f5a9ea3d62b32fb0f))
|
||||||
|
* use sandbox CSP for file previews ([1864951](https://github.com/stonith404/pingvin-share/commit/1864951bdbf573431e795109224a45545b86b54d))
|
||||||
|
|
||||||
## [1.11.1](https://github.com/stonith404/pingvin-share/compare/v1.11.0...v1.11.1) (2025-04-06)
|
## [1.11.1](https://github.com/stonith404/pingvin-share/compare/v1.11.0...v1.11.1) (2025-04-06)
|
||||||
|
|
||||||
## [1.11.0](https://github.com/stonith404/pingvin-share/compare/v1.10.4...v1.11.0) (2025-04-05)
|
## [1.11.0](https://github.com/stonith404/pingvin-share/compare/v1.10.4...v1.11.0) (2025-04-05)
|
||||||
|
|||||||
12
Dockerfile
12
Dockerfile
@@ -1,25 +1,25 @@
|
|||||||
# Stage 1: Frontend dependencies
|
# Stage 1: Frontend dependencies
|
||||||
FROM node:20-alpine AS frontend-dependencies
|
FROM node:22-alpine AS frontend-dependencies
|
||||||
WORKDIR /opt/app
|
WORKDIR /opt/app
|
||||||
COPY frontend/package.json frontend/package-lock.json ./
|
COPY frontend/package.json frontend/package-lock.json ./
|
||||||
RUN npm ci
|
RUN npm ci
|
||||||
|
|
||||||
# Stage 2: Build frontend
|
# Stage 2: Build frontend
|
||||||
FROM node:20-alpine AS frontend-builder
|
FROM node:22-alpine AS frontend-builder
|
||||||
WORKDIR /opt/app
|
WORKDIR /opt/app
|
||||||
COPY ./frontend .
|
COPY ./frontend .
|
||||||
COPY --from=frontend-dependencies /opt/app/node_modules ./node_modules
|
COPY --from=frontend-dependencies /opt/app/node_modules ./node_modules
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
# Stage 3: Backend dependencies
|
# Stage 3: Backend dependencies
|
||||||
FROM node:20-alpine AS backend-dependencies
|
FROM node:22-alpine AS backend-dependencies
|
||||||
RUN apk add --no-cache python3
|
RUN apk add --no-cache python3
|
||||||
WORKDIR /opt/app
|
WORKDIR /opt/app
|
||||||
COPY backend/package.json backend/package-lock.json ./
|
COPY backend/package.json backend/package-lock.json ./
|
||||||
RUN npm ci
|
RUN npm ci
|
||||||
|
|
||||||
# Stage 4: Build backend
|
# Stage 4: Build backend
|
||||||
FROM node:20-alpine AS backend-builder
|
FROM node:22-alpine AS backend-builder
|
||||||
RUN apk add openssl
|
RUN apk add openssl
|
||||||
|
|
||||||
WORKDIR /opt/app
|
WORKDIR /opt/app
|
||||||
@@ -29,7 +29,7 @@ RUN npx prisma generate
|
|||||||
RUN npm run build && npm prune --production
|
RUN npm run build && npm prune --production
|
||||||
|
|
||||||
# Stage 5: Final image
|
# Stage 5: Final image
|
||||||
FROM node:20-alpine AS runner
|
FROM node:22-alpine AS runner
|
||||||
ENV NODE_ENV=docker
|
ENV NODE_ENV=docker
|
||||||
|
|
||||||
# Delete default node user
|
# Delete default node user
|
||||||
@@ -59,7 +59,7 @@ COPY ./scripts/docker ./scripts/docker
|
|||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
HEALTHCHECK --interval=10s --timeout=3s CMD curl -f http://localhost:3000/api/health || exit 1
|
HEALTHCHECK --interval=10s --timeout=3s CMD /bin/sh -c '(if [[ "$CADDY_DISABLED" = "true" ]]; then curl -fs http://localhost:${BACKEND_PORT:-8080}/api/health; else curl -fs http://localhost:3000/api/health; fi) || exit 1'
|
||||||
|
|
||||||
ENTRYPOINT ["sh", "./scripts/docker/create-user.sh"]
|
ENTRYPOINT ["sh", "./scripts/docker/create-user.sh"]
|
||||||
CMD ["sh", "./scripts/docker/entrypoint.sh"]
|
CMD ["sh", "./scripts/docker/entrypoint.sh"]
|
||||||
4189
backend/package-lock.json
generated
4189
backend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pingvin-share-backend",
|
"name": "pingvin-share-backend",
|
||||||
"version": "1.11.1",
|
"version": "1.12.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "nest build",
|
"build": "nest build",
|
||||||
"dev": "cross-env NODE_ENV=development nest start --watch",
|
"dev": "cross-env NODE_ENV=development nest start --watch",
|
||||||
@@ -13,34 +13,34 @@
|
|||||||
"seed": "ts-node prisma/seed/config.seed.ts"
|
"seed": "ts-node prisma/seed/config.seed.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.758.0",
|
"@aws-sdk/client-s3": "^3.787.0",
|
||||||
"@nestjs/cache-manager": "^2.2.2",
|
"@nestjs/cache-manager": "^3.0.1",
|
||||||
"@nestjs/common": "^10.4.3",
|
"@nestjs/common": "^11.0.17",
|
||||||
"@nestjs/config": "^3.2.3",
|
"@nestjs/config": "^4.0.2",
|
||||||
"@nestjs/core": "^10.4.3",
|
"@nestjs/core": "^11.0.17",
|
||||||
"@nestjs/jwt": "^10.2.0",
|
"@nestjs/jwt": "^11.0.0",
|
||||||
"@nestjs/passport": "^10.0.3",
|
"@nestjs/passport": "^11.0.5",
|
||||||
"@nestjs/platform-express": "^10.4.3",
|
"@nestjs/platform-express": "^11.0.17",
|
||||||
"@nestjs/schedule": "^4.1.1",
|
"@nestjs/schedule": "^5.0.1",
|
||||||
"@nestjs/swagger": "^7.4.2",
|
"@nestjs/swagger": "^11.1.3",
|
||||||
"@nestjs/throttler": "^6.2.1",
|
"@nestjs/throttler": "^6.4.0",
|
||||||
"@prisma/client": "^6.4.1",
|
"@prisma/client": "^6.6.0",
|
||||||
"@types/jmespath": "^0.15.2",
|
"@types/jmespath": "^0.15.2",
|
||||||
"archiver": "^7.0.1",
|
"archiver": "^7.0.1",
|
||||||
"argon2": "^0.41.1",
|
"argon2": "^0.41.1",
|
||||||
"body-parser": "^1.20.3",
|
"body-parser": "^2.2.0",
|
||||||
"cache-manager": "^5.7.6",
|
"cache-manager": "^6.4.2",
|
||||||
"clamscan": "^2.3.1",
|
"clamscan": "^2.4.0",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.1",
|
"class-validator": "^0.14.1",
|
||||||
"content-disposition": "^0.5.4",
|
"content-disposition": "^0.5.4",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.7",
|
||||||
"jmespath": "^0.16.0",
|
"jmespath": "^0.16.0",
|
||||||
"ldapts": "^7.2.0",
|
"ldapts": "^7.4.0",
|
||||||
"mime-types": "^2.1.35",
|
"mime-types": "^3.0.1",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"nanoid": "^3.3.7",
|
"nanoid": "^3.3.7",
|
||||||
"nodemailer": "^6.9.15",
|
"nodemailer": "^6.10.1",
|
||||||
"otplib": "^12.0.1",
|
"otplib": "^12.0.1",
|
||||||
"passport": "^0.7.0",
|
"passport": "^0.7.0",
|
||||||
"passport-jwt": "^4.0.1",
|
"passport-jwt": "^4.0.1",
|
||||||
@@ -48,42 +48,43 @@
|
|||||||
"qrcode-svg": "^1.1.0",
|
"qrcode-svg": "^1.1.0",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"rimraf": "^6.0.1",
|
"rimraf": "^6.0.1",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.2",
|
||||||
"sharp": "^0.33.5",
|
"sharp": "^0.34.1",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"yaml": "^2.7.0"
|
"uuid": "^11.1.0",
|
||||||
|
"yaml": "^2.7.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nestjs/cli": "^10.4.5",
|
"@nestjs/cli": "^11.0.6",
|
||||||
"@nestjs/schematics": "^10.1.4",
|
"@nestjs/schematics": "^11.0.5",
|
||||||
"@nestjs/testing": "^10.4.3",
|
"@nestjs/testing": "^11.0.17",
|
||||||
"@types/archiver": "^6.0.2",
|
"@types/archiver": "^6.0.3",
|
||||||
"@types/clamscan": "^2.0.8",
|
"@types/clamscan": "^2.4.1",
|
||||||
"@types/cookie-parser": "^1.4.7",
|
"@types/cookie-parser": "^1.4.8",
|
||||||
"@types/cron": "^2.4.0",
|
"@types/cron": "^2.4.0",
|
||||||
"@types/express": "^4.17.21",
|
"@types/express": "^5.0.1",
|
||||||
"@types/mime-types": "^2.1.4",
|
"@types/mime-types": "^2.1.4",
|
||||||
"@types/multer": "^1.4.12",
|
"@types/multer": "^1.4.12",
|
||||||
"@types/node": "^22.5.5",
|
"@types/node": "^22.14.1",
|
||||||
"@types/nodemailer": "^6.4.16",
|
"@types/nodemailer": "^6.4.17",
|
||||||
"@types/passport-jwt": "^4.0.1",
|
"@types/passport-jwt": "^4.0.1",
|
||||||
"@types/qrcode-svg": "^1.1.5",
|
"@types/qrcode-svg": "^1.1.5",
|
||||||
"@types/sharp": "^0.32.0",
|
"@types/sharp": "^0.32.0",
|
||||||
"@types/supertest": "^6.0.2",
|
"@types/supertest": "^6.0.3",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.6.0",
|
"@typescript-eslint/eslint-plugin": "^8.29.1",
|
||||||
"@typescript-eslint/parser": "^8.6.0",
|
"@typescript-eslint/parser": "^8.29.1",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^9.10.0",
|
"eslint": "^9.24.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^10.1.2",
|
||||||
"eslint-plugin-prettier": "^5.2.1",
|
"eslint-plugin-prettier": "^5.2.6",
|
||||||
"newman": "^6.2.1",
|
"newman": "^6.2.1",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.5.3",
|
||||||
"prisma": "^6.4.1",
|
"prisma": "^6.6.0",
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "^0.5.21",
|
||||||
"ts-loader": "^9.5.1",
|
"ts-loader": "^9.5.2",
|
||||||
"tsconfig-paths": "4.2.0",
|
"tsconfig-paths": "4.2.0",
|
||||||
"typescript": "^5.6.2",
|
"typescript": "^5.8.3",
|
||||||
"wait-on": "^8.0.1"
|
"wait-on": "^8.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export const configVariables = {
|
|||||||
shareRecipientsMessage: {
|
shareRecipientsMessage: {
|
||||||
type: "text",
|
type: "text",
|
||||||
defaultValue:
|
defaultValue:
|
||||||
"Hey!\n\n{creator} ({creatorEmail}) shared some files with you, view or download the files with this link: {shareUrl}\n\nThe share will expire {expires}.\n\nNote: {desc}\n\nShared securely with Pingvin Share 🐧",
|
"Hey!\n\n{creator} ({creatorEmail}) shared some files with you. You can view or download the files with this link: {shareUrl}\n\nThe share will expire {expires}.\n\nNote: {desc}\n\nShared securely with Pingvin Share 🐧",
|
||||||
},
|
},
|
||||||
reverseShareSubject: {
|
reverseShareSubject: {
|
||||||
type: "string",
|
type: "string",
|
||||||
@@ -107,7 +107,7 @@ export const configVariables = {
|
|||||||
resetPasswordMessage: {
|
resetPasswordMessage: {
|
||||||
type: "text",
|
type: "text",
|
||||||
defaultValue:
|
defaultValue:
|
||||||
"Hey!\n\nYou requested a password reset. Click this link to reset your password: {url}\nThe link expires in a hour.\n\nPingvin Share 🐧",
|
"Hey!\n\nYou requested a password reset. Click this link to reset your password: {url}\nThe link expires in an hour.\n\nPingvin Share 🐧",
|
||||||
},
|
},
|
||||||
inviteSubject: {
|
inviteSubject: {
|
||||||
type: "string",
|
type: "string",
|
||||||
|
|||||||
@@ -20,12 +20,12 @@ import { UserModule } from "./user/user.module";
|
|||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
ConfigModule,
|
||||||
AuthModule,
|
AuthModule,
|
||||||
ShareModule,
|
ShareModule,
|
||||||
FileModule,
|
FileModule,
|
||||||
EmailModule,
|
EmailModule,
|
||||||
PrismaModule,
|
PrismaModule,
|
||||||
ConfigModule,
|
|
||||||
JobsModule,
|
JobsModule,
|
||||||
UserModule,
|
UserModule,
|
||||||
ThrottlerModule.forRoot([
|
ThrottlerModule.forRoot([
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Global, Module } from "@nestjs/common";
|
import { Global, Module } from "@nestjs/common";
|
||||||
|
import { Config } from "@prisma/client";
|
||||||
import { EmailModule } from "src/email/email.module";
|
import { EmailModule } from "src/email/email.module";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
import { ConfigController } from "./config.controller";
|
import { ConfigController } from "./config.controller";
|
||||||
@@ -16,7 +17,15 @@ import { LogoService } from "./logo.service";
|
|||||||
},
|
},
|
||||||
inject: [PrismaService],
|
inject: [PrismaService],
|
||||||
},
|
},
|
||||||
ConfigService,
|
{
|
||||||
|
provide: ConfigService,
|
||||||
|
useFactory: async (prisma: PrismaService, configVariables: Config[]) => {
|
||||||
|
const configService = new ConfigService(configVariables, prisma);
|
||||||
|
await configService.initialize();
|
||||||
|
return configService;
|
||||||
|
},
|
||||||
|
inject: [PrismaService, "CONFIG_VARIABLES"],
|
||||||
|
},
|
||||||
LogoService,
|
LogoService,
|
||||||
],
|
],
|
||||||
controllers: [ConfigController],
|
controllers: [ConfigController],
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ export class ConfigService extends EventEmitter {
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
async onModuleInit() {
|
// Initialize gets called by the ConfigModule
|
||||||
|
async initialize() {
|
||||||
await this.loadYamlConfig();
|
await this.loadYamlConfig();
|
||||||
|
|
||||||
if (this.yamlConfig) {
|
if (this.yamlConfig) {
|
||||||
@@ -49,12 +50,13 @@ export class ConfigService extends EventEmitter {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this.yamlConfig = yamlParse(configFile);
|
this.yamlConfig = yamlParse(configFile);
|
||||||
|
|
||||||
if (this.yamlConfig) {
|
if (this.yamlConfig) {
|
||||||
for (const configVariable of this.configVariables) {
|
for (const configVariable of this.configVariables) {
|
||||||
const category = this.yamlConfig[configVariable.category];
|
const category = this.yamlConfig[configVariable.category];
|
||||||
if (!category) continue;
|
if (!category) continue;
|
||||||
|
|
||||||
configVariable.value = category[configVariable.name];
|
configVariable.value = category[configVariable.name];
|
||||||
|
this.emit("update", configVariable.name, configVariable.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export class FileController {
|
|||||||
@Res({ passthrough: true }) res: Response,
|
@Res({ passthrough: true }) res: Response,
|
||||||
@Param("shareId") shareId: string,
|
@Param("shareId") shareId: string,
|
||||||
) {
|
) {
|
||||||
const zipStream = this.fileService.getZip(shareId);
|
const zipStream = await this.fileService.getZip(shareId);
|
||||||
|
|
||||||
res.set({
|
res.set({
|
||||||
"Content-Type": "application/zip",
|
"Content-Type": "application/zip",
|
||||||
@@ -78,7 +78,7 @@ export class FileController {
|
|||||||
"Content-Type":
|
"Content-Type":
|
||||||
mime?.lookup?.(file.metaData.name) || "application/octet-stream",
|
mime?.lookup?.(file.metaData.name) || "application/octet-stream",
|
||||||
"Content-Length": file.metaData.size,
|
"Content-Length": file.metaData.size,
|
||||||
"Content-Security-Policy": "script-src 'none'",
|
"Content-Security-Policy": "sandbox",
|
||||||
};
|
};
|
||||||
|
|
||||||
if (download === "true") {
|
if (download === "true") {
|
||||||
|
|||||||
@@ -59,9 +59,9 @@ export class FileService {
|
|||||||
return storageService.deleteAllFiles(shareId);
|
return storageService.deleteAllFiles(shareId);
|
||||||
}
|
}
|
||||||
|
|
||||||
getZip(shareId: string) {
|
async getZip(shareId: string): Promise<Readable> {
|
||||||
const storageService = this.getStorageService();
|
const storageService = this.getStorageService();
|
||||||
return storageService.getZip(shareId) as Readable;
|
return await storageService.getZip(shareId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async streamToUint8Array(stream: Readable): Promise<Uint8Array> {
|
private async streamToUint8Array(stream: Readable): Promise<Uint8Array> {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { ConfigService } from "src/config/config.service";
|
|||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
import { validate as isValidUUID } from "uuid";
|
import { validate as isValidUUID } from "uuid";
|
||||||
import { SHARE_DIRECTORY } from "../constants";
|
import { SHARE_DIRECTORY } from "../constants";
|
||||||
|
import { Readable } from "stream";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class LocalFileService {
|
export class LocalFileService {
|
||||||
@@ -155,7 +156,19 @@ export class LocalFileService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getZip(shareId: string) {
|
async getZip(shareId: string): Promise<Readable> {
|
||||||
return createReadStream(`${SHARE_DIRECTORY}/${shareId}/archive.zip`);
|
return new Promise((resolve, reject) => {
|
||||||
|
const zipStream = createReadStream(
|
||||||
|
`${SHARE_DIRECTORY}/${shareId}/archive.zip`,
|
||||||
|
);
|
||||||
|
|
||||||
|
zipStream.on("error", (err) => {
|
||||||
|
reject(new InternalServerErrorException(err));
|
||||||
|
});
|
||||||
|
|
||||||
|
zipStream.on("open", () => {
|
||||||
|
resolve(zipStream);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import * as mime from "mime-types";
|
|||||||
import { File } from "./file.service";
|
import { File } from "./file.service";
|
||||||
import { Readable } from "stream";
|
import { Readable } from "stream";
|
||||||
import { validate as isValidUUID } from "uuid";
|
import { validate as isValidUUID } from "uuid";
|
||||||
|
import * as archiver from "archiver";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class S3FileService {
|
export class S3FileService {
|
||||||
@@ -275,7 +276,8 @@ export class S3FileService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getS3Instance(): S3Client {
|
getS3Instance(): S3Client {
|
||||||
const checksumCalculation = this.config.get("s3.useChecksum") === true ? null : "WHEN_REQUIRED";
|
const checksumCalculation =
|
||||||
|
this.config.get("s3.useChecksum") === true ? null : "WHEN_REQUIRED";
|
||||||
|
|
||||||
return new S3Client({
|
return new S3Client({
|
||||||
endpoint: this.config.get("s3.endpoint"),
|
endpoint: this.config.get("s3.endpoint"),
|
||||||
@@ -290,10 +292,95 @@ export class S3FileService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getZip() {
|
getZip(shareId: string) {
|
||||||
throw new BadRequestException(
|
return new Promise<Readable>(async (resolve, reject) => {
|
||||||
"ZIP download is not supported with S3 storage",
|
const s3Instance = this.getS3Instance();
|
||||||
);
|
const bucketName = this.config.get("s3.bucketName");
|
||||||
|
const compressionLevel = this.config.get("share.zipCompressionLevel");
|
||||||
|
|
||||||
|
const prefix = `${this.getS3Path()}${shareId}/`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const listResponse = await s3Instance.send(
|
||||||
|
new ListObjectsV2Command({
|
||||||
|
Bucket: bucketName,
|
||||||
|
Prefix: prefix,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!listResponse.Contents || listResponse.Contents.length === 0) {
|
||||||
|
throw new NotFoundException(`No files found for share ${shareId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const archive = archiver("zip", {
|
||||||
|
zlib: { level: parseInt(compressionLevel) },
|
||||||
|
});
|
||||||
|
|
||||||
|
archive.on("error", (err) => {
|
||||||
|
this.logger.error("Archive error", err);
|
||||||
|
reject(new InternalServerErrorException("Error creating ZIP file"));
|
||||||
|
});
|
||||||
|
|
||||||
|
const fileKeys = listResponse.Contents.filter(
|
||||||
|
(object) => object.Key && object.Key !== prefix,
|
||||||
|
).map((object) => object.Key as string);
|
||||||
|
|
||||||
|
if (fileKeys.length === 0) {
|
||||||
|
throw new NotFoundException(
|
||||||
|
`No valid files found for share ${shareId}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let filesAdded = 0;
|
||||||
|
|
||||||
|
const processNextFile = async (index: number) => {
|
||||||
|
if (index >= fileKeys.length) {
|
||||||
|
archive.finalize();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = fileKeys[index];
|
||||||
|
const fileName = key.replace(prefix, "");
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await s3Instance.send(
|
||||||
|
new GetObjectCommand({
|
||||||
|
Bucket: bucketName,
|
||||||
|
Key: key,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.Body instanceof Readable) {
|
||||||
|
const fileStream = response.Body;
|
||||||
|
|
||||||
|
fileStream.on("end", () => {
|
||||||
|
filesAdded++;
|
||||||
|
processNextFile(index + 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
fileStream.on("error", (err) => {
|
||||||
|
this.logger.error(`Error streaming file ${fileName}`, err);
|
||||||
|
processNextFile(index + 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
archive.append(fileStream, { name: fileName });
|
||||||
|
} else {
|
||||||
|
processNextFile(index + 1);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Error processing file ${fileName}`, error);
|
||||||
|
processNextFile(index + 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
resolve(archive);
|
||||||
|
processNextFile(0);
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error("Error creating ZIP file", error);
|
||||||
|
|
||||||
|
reject(new InternalServerErrorException("Error creating ZIP file"));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getS3Path(): string {
|
getS3Path(): string {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ general:
|
|||||||
secureCookies: "false"
|
secureCookies: "false"
|
||||||
#Whether to show the home page
|
#Whether to show the home page
|
||||||
showHomePage: "true"
|
showHomePage: "true"
|
||||||
#Time in hours after which a user must log in again (default: 3 months).
|
#Time after which a user must log in again (default: 3 months).
|
||||||
sessionDuration: 3 months
|
sessionDuration: 3 months
|
||||||
share:
|
share:
|
||||||
#Whether registration is allowed
|
#Whether registration is allowed
|
||||||
@@ -45,7 +45,7 @@ email:
|
|||||||
Hey!
|
Hey!
|
||||||
|
|
||||||
|
|
||||||
{creator} ({creatorEmail}) shared some files with you, view or download the
|
{creator} ({creatorEmail}) shared some files with you. You can view or download the
|
||||||
files with this link: {shareUrl}
|
files with this link: {shareUrl}
|
||||||
|
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ email:
|
|||||||
You requested a password reset. Click this link to reset your password:
|
You requested a password reset. Click this link to reset your password:
|
||||||
{url}
|
{url}
|
||||||
|
|
||||||
The link expires in a hour.
|
The link expires in an hour.
|
||||||
|
|
||||||
|
|
||||||
Pingvin Share 🐧
|
Pingvin Share 🐧
|
||||||
@@ -103,7 +103,7 @@ smtp:
|
|||||||
host: ""
|
host: ""
|
||||||
#Port of the SMTP server
|
#Port of the SMTP server
|
||||||
port: "0"
|
port: "0"
|
||||||
#Email address from wich the emails get sent
|
#Email address from which the emails get sent
|
||||||
email: ""
|
email: ""
|
||||||
#Username of the SMTP server
|
#Username of the SMTP server
|
||||||
username: ""
|
username: ""
|
||||||
@@ -204,6 +204,8 @@ s3:
|
|||||||
key: ""
|
key: ""
|
||||||
#The secret which allows you to access the S3 bucket.
|
#The secret which allows you to access the S3 bucket.
|
||||||
secret: ""
|
secret: ""
|
||||||
|
#Turn off for backends that do not support checksum (e.g. B2).
|
||||||
|
useChecksum: "true"
|
||||||
legal:
|
legal:
|
||||||
#Whether to show a link to imprint and privacy policy in the footer.
|
#Whether to show a link to imprint and privacy policy in the footer.
|
||||||
enabled: "false"
|
enabled: "false"
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ Your container is now listening on `http://localhost:<externalport>`, have fun w
|
|||||||
|
|
||||||
Required tools:
|
Required tools:
|
||||||
|
|
||||||
- [Node.js](https://nodejs.org/en/download/) >= 16
|
- [Node.js](https://nodejs.org/en/download/) >= 22
|
||||||
- [Git](https://git-scm.com/downloads)
|
- [Git](https://git-scm.com/downloads)
|
||||||
- [pm2](https://pm2.keymetrics.io/) for running Pingvin Share in the background
|
- [pm2](https://pm2.keymetrics.io/) for running Pingvin Share in the background
|
||||||
|
|
||||||
|
|||||||
4
frontend/package-lock.json
generated
4
frontend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "pingvin-share-frontend",
|
"name": "pingvin-share-frontend",
|
||||||
"version": "1.11.1",
|
"version": "1.12.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "pingvin-share-frontend",
|
"name": "pingvin-share-frontend",
|
||||||
"version": "1.11.1",
|
"version": "1.12.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.13.3",
|
"@emotion/react": "^11.13.3",
|
||||||
"@emotion/server": "^11.11.0",
|
"@emotion/server": "^11.11.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pingvin-share-frontend",
|
"name": "pingvin-share-frontend",
|
||||||
"version": "1.11.1",
|
"version": "1.12.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import {
|
|||||||
useMantineTheme,
|
useMantineTheme,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { modals } from "@mantine/modals";
|
import { modals } from "@mantine/modals";
|
||||||
|
import Markdown, { MarkdownToJSX } from "markdown-to-jsx";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import React, { Dispatch, SetStateAction, useEffect, useState } from "react";
|
import React, { Dispatch, SetStateAction, useEffect, useState } from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import api from "../../services/api.service";
|
import api from "../../services/api.service";
|
||||||
import Markdown from "markdown-to-jsx";
|
|
||||||
|
|
||||||
const FilePreviewContext = React.createContext<{
|
const FilePreviewContext = React.createContext<{
|
||||||
shareId: string;
|
shareId: string;
|
||||||
@@ -132,7 +132,8 @@ const TextPreview = () => {
|
|||||||
.then((res) => setText(res.data ?? "Preview couldn't be fetched."));
|
.then((res) => setText(res.data ?? "Preview couldn't be fetched."));
|
||||||
}, [shareId, fileId]);
|
}, [shareId, fileId]);
|
||||||
|
|
||||||
const options = {
|
const options: MarkdownToJSX.Options = {
|
||||||
|
disableParsingRawHTML: true,
|
||||||
overrides: {
|
overrides: {
|
||||||
pre: {
|
pre: {
|
||||||
props: {
|
props: {
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "المشاركة {shareId}",
|
"share.title": "المشاركة {shareId}",
|
||||||
"share.description": "انظر ما الذي شاركته معك!",
|
"share.description": "انظر ما الذي شاركته معك!",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# file} other {# files}} · {size} (zip file may be smaller due to compression)",
|
||||||
"share.error.visitor-limit-exceeded.title": "تم تجاوز حد المشاهدات",
|
"share.error.visitor-limit-exceeded.title": "تم تجاوز حد المشاهدات",
|
||||||
"share.error.visitor-limit-exceeded.description": "تم تجاوز الحد الأقصى لزوار هذه المشاركة.",
|
"share.error.visitor-limit-exceeded.description": "تم تجاوز الحد الأقصى لزوار هذه المشاركة.",
|
||||||
"share.error.removed.title": "تمت إزالة المشاركة",
|
"share.error.removed.title": "تمت إزالة المشاركة",
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "Sdílení {shareId}",
|
"share.title": "Sdílení {shareId}",
|
||||||
"share.description": "Podívejte se, co jsem s vámi sdílel!",
|
"share.description": "Podívejte se, co jsem s vámi sdílel!",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# file} other {# files}} · {size} (zip file may be smaller due to compression)",
|
||||||
"share.error.visitor-limit-exceeded.title": "Limit návštěvníků překročen",
|
"share.error.visitor-limit-exceeded.title": "Limit návštěvníků překročen",
|
||||||
"share.error.visitor-limit-exceeded.description": "Limit návštěvníků tohoto sdílení byl překročen.",
|
"share.error.visitor-limit-exceeded.description": "Limit návštěvníků tohoto sdílení byl překročen.",
|
||||||
"share.error.removed.title": "Sdílení bylo odstraněno",
|
"share.error.removed.title": "Sdílení bylo odstraněno",
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "Del {shareId}",
|
"share.title": "Del {shareId}",
|
||||||
"share.description": "Se hvad jeg har delt med dig!",
|
"share.description": "Se hvad jeg har delt med dig!",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# file} other {# files}} · {size} (zip file may be smaller due to compression)",
|
||||||
"share.error.visitor-limit-exceeded.title": "Grænsen for besøgende overskredet",
|
"share.error.visitor-limit-exceeded.title": "Grænsen for besøgende overskredet",
|
||||||
"share.error.visitor-limit-exceeded.description": "Besøgsgrænsen for denne deling er blevet overskredet.",
|
"share.error.visitor-limit-exceeded.description": "Besøgsgrænsen for denne deling er blevet overskredet.",
|
||||||
"share.error.removed.title": "Deling fjernet",
|
"share.error.removed.title": "Deling fjernet",
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "Freigabe {shareId}",
|
"share.title": "Freigabe {shareId}",
|
||||||
"share.description": "Schau, was ich mit dir geteilt habe!",
|
"share.description": "Schau, was ich mit dir geteilt habe!",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# file} other {# files}} · {size} (zip file may be smaller due to compression)",
|
||||||
"share.error.visitor-limit-exceeded.title": "Besucher Limit erreicht",
|
"share.error.visitor-limit-exceeded.title": "Besucher Limit erreicht",
|
||||||
"share.error.visitor-limit-exceeded.description": "Die maximale Besucheranzahl für diese Freigabe ist überschritten.",
|
"share.error.visitor-limit-exceeded.description": "Die maximale Besucheranzahl für diese Freigabe ist überschritten.",
|
||||||
"share.error.removed.title": "Freigabe entfernt",
|
"share.error.removed.title": "Freigabe entfernt",
|
||||||
@@ -525,7 +526,7 @@ export default {
|
|||||||
"common.error.unknown": "Ein unbekannter Fehler ist aufgetreten",
|
"common.error.unknown": "Ein unbekannter Fehler ist aufgetreten",
|
||||||
"common.error.invalid-email": "Ungültige E-Mail-Adresse",
|
"common.error.invalid-email": "Ungültige E-Mail-Adresse",
|
||||||
"common.error.too-short": "Muss mindestens {length} Zeichen enthalten",
|
"common.error.too-short": "Muss mindestens {length} Zeichen enthalten",
|
||||||
"common.error.too-long": "Muss maximal {length} Zeichen enthalten",
|
"common.error.too-long": "Darf maximal {length} Zeichen enthalten",
|
||||||
"common.error.number-too-small": "Darf mindestens {min} sein",
|
"common.error.number-too-small": "Darf mindestens {min} sein",
|
||||||
"common.error.number-too-large": "Darf höchstens {max} sein",
|
"common.error.number-too-large": "Darf höchstens {max} sein",
|
||||||
"common.error.exact-length": "Muss genau {length} Zeichen lang sein",
|
"common.error.exact-length": "Muss genau {length} Zeichen lang sein",
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "Διαμοιρασμός {shareId}",
|
"share.title": "Διαμοιρασμός {shareId}",
|
||||||
"share.description": "Σας προωθώ αρχεία προς κοινοποίηση.",
|
"share.description": "Σας προωθώ αρχεία προς κοινοποίηση.",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# file} other {# files}} · {size} (zip file may be smaller due to compression)",
|
||||||
"share.error.visitor-limit-exceeded.title": "Υπέρβαση ορίου επισκέπτη",
|
"share.error.visitor-limit-exceeded.title": "Υπέρβαση ορίου επισκέπτη",
|
||||||
"share.error.visitor-limit-exceeded.description": "Ξεπεράστηκε το όριο επισκεπτών σε αυτή την κοινοποίηση.",
|
"share.error.visitor-limit-exceeded.description": "Ξεπεράστηκε το όριο επισκεπτών σε αυτή την κοινοποίηση.",
|
||||||
"share.error.removed.title": "Κοινοποίηση αφαιρέθηκε",
|
"share.error.removed.title": "Κοινοποίηση αφαιρέθηκε",
|
||||||
|
|||||||
@@ -366,6 +366,8 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "Share {shareId}",
|
"share.title": "Share {shareId}",
|
||||||
"share.description": "Look what I've shared with you!",
|
"share.description": "Look what I've shared with you!",
|
||||||
|
"share.fileCount":
|
||||||
|
"{count, plural, =1 {# file} other {# files}} · {size} (zip file may be smaller due to compression)",
|
||||||
"share.error.visitor-limit-exceeded.title": "Visitor limit exceeded",
|
"share.error.visitor-limit-exceeded.title": "Visitor limit exceeded",
|
||||||
"share.error.visitor-limit-exceeded.description":
|
"share.error.visitor-limit-exceeded.description":
|
||||||
"The visitor limit from this share has been exceeded.",
|
"The visitor limit from this share has been exceeded.",
|
||||||
@@ -415,7 +417,8 @@ export default {
|
|||||||
|
|
||||||
// /admin/config
|
// /admin/config
|
||||||
"admin.config.config-file-warning.title": "Configuration file present",
|
"admin.config.config-file-warning.title": "Configuration file present",
|
||||||
"admin.config.config-file-warning.description": "As you have a configured Pingvin Share with a configuration file, you can't change the configuration through the UI.",
|
"admin.config.config-file-warning.description":
|
||||||
|
"As you have a configured Pingvin Share with a configuration file, you can't change the configuration through the UI.",
|
||||||
|
|
||||||
"admin.config.title": "Configuration",
|
"admin.config.title": "Configuration",
|
||||||
"admin.config.category.general": "General",
|
"admin.config.category.general": "General",
|
||||||
@@ -642,7 +645,8 @@ export default {
|
|||||||
|
|
||||||
"admin.config.category.s3": "S3",
|
"admin.config.category.s3": "S3",
|
||||||
"admin.config.s3.enabled": "Enabled",
|
"admin.config.s3.enabled": "Enabled",
|
||||||
"admin.config.s3.enabled.description": "Whether S3 should be used to store the shared files instead of the local file system.",
|
"admin.config.s3.enabled.description":
|
||||||
|
"Whether S3 should be used to store the shared files instead of the local file system.",
|
||||||
"admin.config.s3.endpoint": "Endpoint",
|
"admin.config.s3.endpoint": "Endpoint",
|
||||||
"admin.config.s3.endpoint.description": "The URL of the S3 bucket.",
|
"admin.config.s3.endpoint.description": "The URL of the S3 bucket.",
|
||||||
"admin.config.s3.region": "Region",
|
"admin.config.s3.region": "Region",
|
||||||
@@ -650,25 +654,34 @@ export default {
|
|||||||
"admin.config.s3.bucket-name": "Bucket name",
|
"admin.config.s3.bucket-name": "Bucket name",
|
||||||
"admin.config.s3.bucket-name.description": "The name of the S3 bucket.",
|
"admin.config.s3.bucket-name.description": "The name of the S3 bucket.",
|
||||||
"admin.config.s3.bucket-path": "Path",
|
"admin.config.s3.bucket-path": "Path",
|
||||||
"admin.config.s3.bucket-path.description": "The default path which should be used to store the files in the S3 bucket.",
|
"admin.config.s3.bucket-path.description":
|
||||||
|
"The default path which should be used to store the files in the S3 bucket.",
|
||||||
"admin.config.s3.key": "Key",
|
"admin.config.s3.key": "Key",
|
||||||
"admin.config.s3.key.description": "The key which allows you to access the S3 bucket.",
|
"admin.config.s3.key.description":
|
||||||
|
"The key which allows you to access the S3 bucket.",
|
||||||
"admin.config.s3.secret": "Secret",
|
"admin.config.s3.secret": "Secret",
|
||||||
"admin.config.s3.secret.description": "The secret which allows you to access the S3 bucket.",
|
"admin.config.s3.secret.description":
|
||||||
|
"The secret which allows you to access the S3 bucket.",
|
||||||
"admin.config.s3.use-checksum": "Use checksum",
|
"admin.config.s3.use-checksum": "Use checksum",
|
||||||
"admin.config.s3.use-checksum.description": "Turn off for backends that do not support checksum (e.g. B2).",
|
"admin.config.s3.use-checksum.description":
|
||||||
|
"Turn off for backends that do not support checksum (e.g. B2).",
|
||||||
|
|
||||||
"admin.config.category.legal": "Legal",
|
"admin.config.category.legal": "Legal",
|
||||||
"admin.config.legal.enabled": "Enable legal notices",
|
"admin.config.legal.enabled": "Enable legal notices",
|
||||||
"admin.config.legal.enabled.description": "Whether to show a link to imprint and privacy policy in the footer.",
|
"admin.config.legal.enabled.description":
|
||||||
|
"Whether to show a link to imprint and privacy policy in the footer.",
|
||||||
"admin.config.legal.imprint-text": "Imprint text",
|
"admin.config.legal.imprint-text": "Imprint text",
|
||||||
"admin.config.legal.imprint-text.description": "The text which should be shown in the imprint. Supports Markdown. Leave blank to link to an external imprint page.",
|
"admin.config.legal.imprint-text.description":
|
||||||
|
"The text which should be shown in the imprint. Supports Markdown. Leave blank to link to an external imprint page.",
|
||||||
"admin.config.legal.imprint-url": "Imprint URL",
|
"admin.config.legal.imprint-url": "Imprint URL",
|
||||||
"admin.config.legal.imprint-url.description": "If you already have an imprint page you can link it here instead of using the text field.",
|
"admin.config.legal.imprint-url.description":
|
||||||
|
"If you already have an imprint page you can link it here instead of using the text field.",
|
||||||
"admin.config.legal.privacy-policy-text": "Privacy policy text",
|
"admin.config.legal.privacy-policy-text": "Privacy policy text",
|
||||||
"admin.config.legal.privacy-policy-text.description": "The text which should be shown in the privacy policy. Supports Markdown. Leave blank to link to an external privacy policy page.",
|
"admin.config.legal.privacy-policy-text.description":
|
||||||
|
"The text which should be shown in the privacy policy. Supports Markdown. Leave blank to link to an external privacy policy page.",
|
||||||
"admin.config.legal.privacy-policy-url": "Privacy policy URL",
|
"admin.config.legal.privacy-policy-url": "Privacy policy URL",
|
||||||
"admin.config.legal.privacy-policy-url.description": "If you already have a privacy policy page you can link it here instead of using the text field.",
|
"admin.config.legal.privacy-policy-url.description":
|
||||||
|
"If you already have a privacy policy page you can link it here instead of using the text field.",
|
||||||
|
|
||||||
// 404
|
// 404
|
||||||
"404.description": "Oops this page doesn't exist.",
|
"404.description": "Oops this page doesn't exist.",
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "Compartido {shareId}",
|
"share.title": "Compartido {shareId}",
|
||||||
"share.description": "¡Mira lo que he compartido contigo!",
|
"share.description": "¡Mira lo que he compartido contigo!",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# file} other {# files}} · {size} (zip file may be smaller due to compression)",
|
||||||
"share.error.visitor-limit-exceeded.title": "Se excedió el límite de visitas",
|
"share.error.visitor-limit-exceeded.title": "Se excedió el límite de visitas",
|
||||||
"share.error.visitor-limit-exceeded.description": "Se ha excedido el límite de visitas para este compartido.",
|
"share.error.visitor-limit-exceeded.description": "Se ha excedido el límite de visitas para este compartido.",
|
||||||
"share.error.removed.title": "Compartido eliminado",
|
"share.error.removed.title": "Compartido eliminado",
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "Jagamine {shareId}",
|
"share.title": "Jagamine {shareId}",
|
||||||
"share.description": "Vaata, mida ma sinuga jagasin!",
|
"share.description": "Vaata, mida ma sinuga jagasin!",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# file} other {# files}} · {size} (zip file may be smaller due to compression)",
|
||||||
"share.error.visitor-limit-exceeded.title": "Külastajate limiit ületatud",
|
"share.error.visitor-limit-exceeded.title": "Külastajate limiit ületatud",
|
||||||
"share.error.visitor-limit-exceeded.description": "Selle jagamise külastajate limiit on ületatud.",
|
"share.error.visitor-limit-exceeded.description": "Selle jagamise külastajate limiit on ületatud.",
|
||||||
"share.error.removed.title": "Jagamine eemaldatud",
|
"share.error.removed.title": "Jagamine eemaldatud",
|
||||||
@@ -465,8 +466,8 @@ export default {
|
|||||||
"admin.config.s3.key.description": "S3 ämbri võti.",
|
"admin.config.s3.key.description": "S3 ämbri võti.",
|
||||||
"admin.config.s3.secret": "Saladus",
|
"admin.config.s3.secret": "Saladus",
|
||||||
"admin.config.s3.secret.description": "S3 ämbri saladus.",
|
"admin.config.s3.secret.description": "S3 ämbri saladus.",
|
||||||
"admin.config.s3.use-checksum": "Use checksum",
|
"admin.config.s3.use-checksum": "Kasuta kontrollsummat",
|
||||||
"admin.config.s3.use-checksum.description": "Turn off for backends that do not support checksum (e.g. B2).",
|
"admin.config.s3.use-checksum.description": "Lülita välja, kui tagaliides ei toeta kontrollsummasid (nt B2).",
|
||||||
"admin.config.category.legal": "Juriidiline",
|
"admin.config.category.legal": "Juriidiline",
|
||||||
"admin.config.legal.enabled": "Luba juriidilised teated",
|
"admin.config.legal.enabled": "Luba juriidilised teated",
|
||||||
"admin.config.legal.enabled.description": "Kas kuvada linki kontaktandmetele ja privaatsuspoliitikale jaluses.",
|
"admin.config.legal.enabled.description": "Kas kuvada linki kontaktandmetele ja privaatsuspoliitikale jaluses.",
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "Jaa {shareId}",
|
"share.title": "Jaa {shareId}",
|
||||||
"share.description": "Katso, mitä olen jakanut kanssasi!",
|
"share.description": "Katso, mitä olen jakanut kanssasi!",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# file} other {# files}} · {size} (zip file may be smaller due to compression)",
|
||||||
"share.error.visitor-limit-exceeded.title": "Vierailijoiden raja ylitetty",
|
"share.error.visitor-limit-exceeded.title": "Vierailijoiden raja ylitetty",
|
||||||
"share.error.visitor-limit-exceeded.description": "Tämän jaon kävijäraja on ylittynyt.",
|
"share.error.visitor-limit-exceeded.description": "Tämän jaon kävijäraja on ylittynyt.",
|
||||||
"share.error.removed.title": "Jako poistettu",
|
"share.error.removed.title": "Jako poistettu",
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "Partage {shareId}",
|
"share.title": "Partage {shareId}",
|
||||||
"share.description": "Regardez ce que j’ai partagé !",
|
"share.description": "Regardez ce que j’ai partagé !",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# fichier} other {# fichiers}} · {size} (le fichier compressé peut être plus petit)",
|
||||||
"share.error.visitor-limit-exceeded.title": "Limite de visiteurs dépassée",
|
"share.error.visitor-limit-exceeded.title": "Limite de visiteurs dépassée",
|
||||||
"share.error.visitor-limit-exceeded.description": "La limite de visiteurs de ce partage a été dépassée.",
|
"share.error.visitor-limit-exceeded.description": "La limite de visiteurs de ce partage a été dépassée.",
|
||||||
"share.error.removed.title": "Partage supprimé",
|
"share.error.removed.title": "Partage supprimé",
|
||||||
@@ -465,8 +466,8 @@ export default {
|
|||||||
"admin.config.s3.key.description": "La clé qui vous permet d'accéder au bucket S3.",
|
"admin.config.s3.key.description": "La clé qui vous permet d'accéder au bucket S3.",
|
||||||
"admin.config.s3.secret": "Secret",
|
"admin.config.s3.secret": "Secret",
|
||||||
"admin.config.s3.secret.description": "Le secret qui vous permet d'accéder au bucket S3.",
|
"admin.config.s3.secret.description": "Le secret qui vous permet d'accéder au bucket S3.",
|
||||||
"admin.config.s3.use-checksum": "Use checksum",
|
"admin.config.s3.use-checksum": "Utiliser la somme de contrôle",
|
||||||
"admin.config.s3.use-checksum.description": "Turn off for backends that do not support checksum (e.g. B2).",
|
"admin.config.s3.use-checksum.description": "Désactivez cette option pour les backends qui ne prennent pas en charge la somme de contrôle (ex. B2).",
|
||||||
"admin.config.category.legal": "Juridique",
|
"admin.config.category.legal": "Juridique",
|
||||||
"admin.config.legal.enabled": "Activer les mentions légales",
|
"admin.config.legal.enabled": "Activer les mentions légales",
|
||||||
"admin.config.legal.enabled.description": "Afficher/Masquer dans le pied de page un lien vers les mentions légales et la politique de confidentialité.",
|
"admin.config.legal.enabled.description": "Afficher/Masquer dans le pied de page un lien vers les mentions légales et la politique de confidentialité.",
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "Dijeljenje {shareId}",
|
"share.title": "Dijeljenje {shareId}",
|
||||||
"share.description": "Pogledajte što sam podijelio s vama!",
|
"share.description": "Pogledajte što sam podijelio s vama!",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# file} other {# files}} · {size} (zip file may be smaller due to compression)",
|
||||||
"share.error.visitor-limit-exceeded.title": "Prekoračeno ograničenje posjetitelja",
|
"share.error.visitor-limit-exceeded.title": "Prekoračeno ograničenje posjetitelja",
|
||||||
"share.error.visitor-limit-exceeded.description": "Ograničenje broja posjetitelja za ovo dijeljenje je premašeno.",
|
"share.error.visitor-limit-exceeded.description": "Ograničenje broja posjetitelja za ovo dijeljenje je premašeno.",
|
||||||
"share.error.removed.title": "Dijeljenje je uklonjeno",
|
"share.error.removed.title": "Dijeljenje je uklonjeno",
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "Megosztás: {shareId}",
|
"share.title": "Megosztás: {shareId}",
|
||||||
"share.description": "Megosztottak Önnel valami fontosat!",
|
"share.description": "Megosztottak Önnel valami fontosat!",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# file} other {# files}} · {size} (zip file may be smaller due to compression)",
|
||||||
"share.error.visitor-limit-exceeded.title": "Túl sok látogató",
|
"share.error.visitor-limit-exceeded.title": "Túl sok látogató",
|
||||||
"share.error.visitor-limit-exceeded.description": "A látogatók száma elérte a megszabott korlátot.",
|
"share.error.visitor-limit-exceeded.description": "A látogatók száma elérte a megszabott korlátot.",
|
||||||
"share.error.removed.title": "Korábban már eltávolított megosztás",
|
"share.error.removed.title": "Korábban már eltávolított megosztás",
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "Condividi {shareId}",
|
"share.title": "Condividi {shareId}",
|
||||||
"share.description": "Guarda cosa ho condiviso con te!",
|
"share.description": "Guarda cosa ho condiviso con te!",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# file} other {# file}} · {size} (il file zip potrebbe essere più piccolo a causa della compressione)",
|
||||||
"share.error.visitor-limit-exceeded.title": "Limite visitatori superato",
|
"share.error.visitor-limit-exceeded.title": "Limite visitatori superato",
|
||||||
"share.error.visitor-limit-exceeded.description": "Il limite di visitatori di questa condivisione è stato superato.",
|
"share.error.visitor-limit-exceeded.description": "Il limite di visitatori di questa condivisione è stato superato.",
|
||||||
"share.error.removed.title": "Condivisione rimossa",
|
"share.error.removed.title": "Condivisione rimossa",
|
||||||
@@ -465,8 +466,8 @@ export default {
|
|||||||
"admin.config.s3.key.description": "La chiave che consente di accedere al bucket S3.",
|
"admin.config.s3.key.description": "La chiave che consente di accedere al bucket S3.",
|
||||||
"admin.config.s3.secret": "Parola chiave",
|
"admin.config.s3.secret": "Parola chiave",
|
||||||
"admin.config.s3.secret.description": "La parola chiave che consente di accedere al bucket S3.",
|
"admin.config.s3.secret.description": "La parola chiave che consente di accedere al bucket S3.",
|
||||||
"admin.config.s3.use-checksum": "Use checksum",
|
"admin.config.s3.use-checksum": "Usa il checksum",
|
||||||
"admin.config.s3.use-checksum.description": "Turn off for backends that do not support checksum (e.g. B2).",
|
"admin.config.s3.use-checksum.description": "Disattiva per i backend che non supportano il checksum (ad es. B2).",
|
||||||
"admin.config.category.legal": "Legale",
|
"admin.config.category.legal": "Legale",
|
||||||
"admin.config.legal.enabled": "Abilita le notifiche legali",
|
"admin.config.legal.enabled": "Abilita le notifiche legali",
|
||||||
"admin.config.legal.enabled.description": "Indica se mostrare o meno un link alle informative legali e sulla privacy a piè di pagina.",
|
"admin.config.legal.enabled.description": "Indica se mostrare o meno un link alle informative legali e sulla privacy a piè di pagina.",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export default {
|
|||||||
"navbar.links.reverse": "ファイルリクエスト",
|
"navbar.links.reverse": "ファイルリクエスト",
|
||||||
"navbar.avatar.account": "マイアカウント",
|
"navbar.avatar.account": "マイアカウント",
|
||||||
"navbar.avatar.admin": "管理画面",
|
"navbar.avatar.admin": "管理画面",
|
||||||
"navbar.avatar.signout": "サインアウト",
|
"navbar.avatar.signout": "ログアウト",
|
||||||
// END navbar
|
// END navbar
|
||||||
// /
|
// /
|
||||||
"home.title": "<h>セルフホスト</h>のファイル共有プラットフォーム。",
|
"home.title": "<h>セルフホスト</h>のファイル共有プラットフォーム。",
|
||||||
@@ -25,16 +25,16 @@ export default {
|
|||||||
// /auth/signin
|
// /auth/signin
|
||||||
"signin.title": "おかえりなさい",
|
"signin.title": "おかえりなさい",
|
||||||
"signin.description": "アカウントをお持ちではありませんか?",
|
"signin.description": "アカウントをお持ちではありませんか?",
|
||||||
"signin.button.signup": "会員登録",
|
"signin.button.signup": "新規登録",
|
||||||
"signin.input.email-or-username": "メールアドレスまたはユーザー名",
|
"signin.input.email-or-username": "メールアドレスまたはユーザー名",
|
||||||
"signin.input.email-or-username.placeholder": "メールアドレスまたはユーザー名",
|
"signin.input.email-or-username.placeholder": "メールアドレスまたはユーザー名",
|
||||||
"signin.input.password": "パスワード",
|
"signin.input.password": "パスワード",
|
||||||
"signin.input.password.placeholder": "あなたのパスワード",
|
"signin.input.password.placeholder": "あなたのパスワード",
|
||||||
"signin.button.submit": "サインイン",
|
"signin.button.submit": "ログイン",
|
||||||
"signIn.notify.totp-required.title": "二段階認証が必要です",
|
"signIn.notify.totp-required.title": "二段階認証が必要です",
|
||||||
"signIn.notify.totp-required.description": "二段階認証コードを入力してください",
|
"signIn.notify.totp-required.description": "二段階認証コードを入力してください",
|
||||||
"signIn.oauth.or": "または",
|
"signIn.oauth.or": "または",
|
||||||
"signIn.oauth.signInWith": "サインインの方法",
|
"signIn.oauth.signInWith": "ログインの方法",
|
||||||
"signIn.oauth.github": "GitHub",
|
"signIn.oauth.github": "GitHub",
|
||||||
"signIn.oauth.google": "Google",
|
"signIn.oauth.google": "Google",
|
||||||
"signIn.oauth.microsoft": "Microsoft",
|
"signIn.oauth.microsoft": "Microsoft",
|
||||||
@@ -44,7 +44,7 @@ export default {
|
|||||||
// /auth/signup
|
// /auth/signup
|
||||||
"signup.title": "アカウントを作成",
|
"signup.title": "アカウントを作成",
|
||||||
"signup.description": "既にアカウントをお持ちですか?",
|
"signup.description": "既にアカウントをお持ちですか?",
|
||||||
"signup.button.signin": "サインイン",
|
"signup.button.signin": "ログイン",
|
||||||
"signup.input.username": "ユーザー名",
|
"signup.input.username": "ユーザー名",
|
||||||
"signup.input.username.placeholder": "あなたのユーザー名",
|
"signup.input.username.placeholder": "あなたのユーザー名",
|
||||||
"signup.input.email": "メールアドレス",
|
"signup.input.email": "メールアドレス",
|
||||||
@@ -53,13 +53,13 @@ export default {
|
|||||||
// END /auth/signup
|
// END /auth/signup
|
||||||
// /auth/totp
|
// /auth/totp
|
||||||
"totp.title": "二段階認証",
|
"totp.title": "二段階認証",
|
||||||
"totp.button.signIn": "サインイン",
|
"totp.button.signIn": "ログイン",
|
||||||
// END /auth/totp
|
// END /auth/totp
|
||||||
// /auth/reset-password
|
// /auth/reset-password
|
||||||
"resetPassword.title": "パスワードを忘れてしまいましたか?",
|
"resetPassword.title": "パスワードを忘れてしまいましたか?",
|
||||||
"resetPassword.description": "登録しているメールアドレスを入力してください。",
|
"resetPassword.description": "登録しているメールアドレスを入力してください。",
|
||||||
"resetPassword.notify.success": "指定されたメールアドレスが存在する場合、パスワードをリセットするためのリンクを含むメッセージが送信されました。",
|
"resetPassword.notify.success": "指定されたメールアドレスが存在する場合、パスワードをリセットするためのリンクを含むメッセージが送信されました。",
|
||||||
"resetPassword.button.back": "サインインページに戻る",
|
"resetPassword.button.back": "ログインページに戻る",
|
||||||
"resetPassword.text.resetPassword": "パスワードをリセット",
|
"resetPassword.text.resetPassword": "パスワードをリセット",
|
||||||
"resetPassword.text.enterNewPassword": "新規パスワードを入力",
|
"resetPassword.text.enterNewPassword": "新規パスワードを入力",
|
||||||
"resetPassword.input.password": "新規パスワード",
|
"resetPassword.input.password": "新規パスワード",
|
||||||
@@ -85,11 +85,11 @@ export default {
|
|||||||
"account.card.oauth.unlink": "リンク解除",
|
"account.card.oauth.unlink": "リンク解除",
|
||||||
"account.card.oauth.unlinked": "リンクされていません",
|
"account.card.oauth.unlinked": "リンクされていません",
|
||||||
"account.modal.unlink.title": "アカウントのリンクを解除",
|
"account.modal.unlink.title": "アカウントのリンクを解除",
|
||||||
"account.modal.unlink.description": "ソーシャルアカウントのリンクを解除すると、ログイン認証情報を忘れた場合、アカウントを失う可能性があります。",
|
"account.modal.unlink.description": "ソーシャルアカウントのリンクを解除すると、ログイン認証情報を忘れた場合にカウントへアクセスできなくなる可能性があります。",
|
||||||
"account.notify.oauth.unlinked.success": "リンク解除に成功しました",
|
"account.notify.oauth.unlinked.success": "リンク解除に成功しました",
|
||||||
"account.card.security.title": "セキュリティ",
|
"account.card.security.title": "セキュリティ",
|
||||||
"account.card.security.totp.enable.description": "2段階認証を有効にするため、現在のパスワードを入力してください",
|
"account.card.security.totp.enable.description": "2段階認証を有効にするには、現在のパスワードを入力してください",
|
||||||
"account.card.security.totp.disable.description": "2段階認証を無効にするため、現在のパスワードを入力してください",
|
"account.card.security.totp.disable.description": "2段階認証を無効にするには、現在のパスワードを入力してください",
|
||||||
"account.card.security.totp.button.start": "開始",
|
"account.card.security.totp.button.start": "開始",
|
||||||
"account.modal.totp.title": "2段階認証を有効にする",
|
"account.modal.totp.title": "2段階認証を有効にする",
|
||||||
"account.modal.totp.step1": "ステップ1: 認証アプリを追加する",
|
"account.modal.totp.step1": "ステップ1: 認証アプリを追加する",
|
||||||
@@ -216,7 +216,7 @@ export default {
|
|||||||
// /upload
|
// /upload
|
||||||
"upload.title": "アップロード",
|
"upload.title": "アップロード",
|
||||||
"upload.notify.confirm-leave": "このページから離れますか?アップロードはキャンセルされます。",
|
"upload.notify.confirm-leave": "このページから離れますか?アップロードはキャンセルされます。",
|
||||||
"upload.notify.generic-error": "共有を仕上げている最中にエラーが発生しました。",
|
"upload.notify.generic-error": "共有の最終処理でエラーが発生しました。",
|
||||||
"upload.notify.count-failed": "{count} ファイルがアップロードに失敗しました。再度お試しください。",
|
"upload.notify.count-failed": "{count} ファイルがアップロードに失敗しました。再度お試しください。",
|
||||||
"upload.reverse-share.error.invalid.title": "無効なファイルリクエストリンク",
|
"upload.reverse-share.error.invalid.title": "無効なファイルリクエストリンク",
|
||||||
"upload.reverse-share.error.invalid.description": "このファイルリクエストは期限が切れているか無効です。",
|
"upload.reverse-share.error.invalid.description": "このファイルリクエストは期限が切れているか無効です。",
|
||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "「{shareId}」が共有されました",
|
"share.title": "「{shareId}」が共有されました",
|
||||||
"share.description": "あなたと共有したファイルをご確認ください!",
|
"share.description": "あなたと共有したファイルをご確認ください!",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# ファイル} other {# ファイル}} · {size} (圧縮により ZIP ファイルはより小さくなる場合があります)",
|
||||||
"share.error.visitor-limit-exceeded.title": "訪問者の上限を超えました",
|
"share.error.visitor-limit-exceeded.title": "訪問者の上限を超えました",
|
||||||
"share.error.visitor-limit-exceeded.description": "この共有からの訪問者の回数が制限を超えています。",
|
"share.error.visitor-limit-exceeded.description": "この共有からの訪問者の回数が制限を超えています。",
|
||||||
"share.error.removed.title": "共有が削除されました",
|
"share.error.removed.title": "共有が削除されました",
|
||||||
@@ -419,7 +420,7 @@ export default {
|
|||||||
"admin.config.oauth.oidc-username-claim": "OpenID Connect ユーザー名の要求",
|
"admin.config.oauth.oidc-username-claim": "OpenID Connect ユーザー名の要求",
|
||||||
"admin.config.oauth.oidc-username-claim.description": "OpenID Connect ID トークンのユーザー名要求。この設定が何かわからない場合は空白のままにしてください。",
|
"admin.config.oauth.oidc-username-claim.description": "OpenID Connect ID トークンのユーザー名要求。この設定が何かわからない場合は空白のままにしてください。",
|
||||||
"admin.config.oauth.oidc-role-path": "OpenID Connectトークンのロールへのパス",
|
"admin.config.oauth.oidc-role-path": "OpenID Connectトークンのロールへのパス",
|
||||||
"admin.config.oauth.oidc-role-path.description": "ロールの配列を参照する有効なJMESパスでなければなりません。" + "OpenID Connectのロールを使用してアクセス権を管理することは、他のIDプロバイダが設定されておらず、パスワードログインが無効になっている場合にのみ推奨されます。この構成がわからない場合は空白のままにしてください。" + "この設定が何であるか分からない場合は空白のままにしてください。",
|
"admin.config.oauth.oidc-role-path.description": "ロールの配列を参照する有効なJMESパスでなければなりません。 " + "OpenID Connectのロールを使用してアクセス権を管理することは、他のIDプロバイダが設定されておらず、パスワードログインが無効になっている場合にのみ推奨されます。この構成がわからない場合は空白のままにしてください。 " + "この設定が何であるか分からない場合は空白のままにしてください。",
|
||||||
"admin.config.oauth.oidc-role-general-access": "一般的なアクセスのためのOpenID Connectのロール",
|
"admin.config.oauth.oidc-role-general-access": "一般的なアクセスのためのOpenID Connectのロール",
|
||||||
"admin.config.oauth.oidc-role-general-access.description": "一般的なアクセスに必要なロール。ログインするユーザーのロールに存在する必要があります。 " + "この設定が何であるか分からない場合は空白のままにしてください。",
|
"admin.config.oauth.oidc-role-general-access.description": "一般的なアクセスに必要なロール。ログインするユーザーのロールに存在する必要があります。 " + "この設定が何であるか分からない場合は空白のままにしてください。",
|
||||||
"admin.config.oauth.oidc-role-admin-access": "管理者アクセスのための OpenID Connectのロール",
|
"admin.config.oauth.oidc-role-admin-access": "管理者アクセスのための OpenID Connectのロール",
|
||||||
@@ -465,8 +466,8 @@ export default {
|
|||||||
"admin.config.s3.key.description": "S3バケットにアクセスするためのキー。",
|
"admin.config.s3.key.description": "S3バケットにアクセスするためのキー。",
|
||||||
"admin.config.s3.secret": "秘密",
|
"admin.config.s3.secret": "秘密",
|
||||||
"admin.config.s3.secret.description": "S3バケットにアクセスするための秘密。",
|
"admin.config.s3.secret.description": "S3バケットにアクセスするための秘密。",
|
||||||
"admin.config.s3.use-checksum": "Use checksum",
|
"admin.config.s3.use-checksum": "チェックサムを使用",
|
||||||
"admin.config.s3.use-checksum.description": "Turn off for backends that do not support checksum (e.g. B2).",
|
"admin.config.s3.use-checksum.description": "チェックサムをサポートしていないバックエンド(例:B2)では無効にしてください。",
|
||||||
"admin.config.category.legal": "法的事項",
|
"admin.config.category.legal": "法的事項",
|
||||||
"admin.config.legal.enabled": "法的通知を有効にする",
|
"admin.config.legal.enabled": "法的通知を有効にする",
|
||||||
"admin.config.legal.enabled.description": "フッターにインプリントとプライバシーポリシーへのリンクを表示するか。",
|
"admin.config.legal.enabled.description": "フッターにインプリントとプライバシーポリシーへのリンクを表示するか。",
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "공유 {shareId}",
|
"share.title": "공유 {shareId}",
|
||||||
"share.description": "내가 당신과 공유한 것을 보세요!",
|
"share.description": "내가 당신과 공유한 것을 보세요!",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# file} other {# files}} · {size} (zip file may be smaller due to compression)",
|
||||||
"share.error.visitor-limit-exceeded.title": "방문자 한도 초과",
|
"share.error.visitor-limit-exceeded.title": "방문자 한도 초과",
|
||||||
"share.error.visitor-limit-exceeded.description": "이 공유의 방문자 한도를 초과했습니다.",
|
"share.error.visitor-limit-exceeded.description": "이 공유의 방문자 한도를 초과했습니다.",
|
||||||
"share.error.removed.title": "공유가 삭제됨",
|
"share.error.removed.title": "공유가 삭제됨",
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "Share {shareId}",
|
"share.title": "Share {shareId}",
|
||||||
"share.description": "Kijk eens wat ik met je heb gedeeld!",
|
"share.description": "Kijk eens wat ik met je heb gedeeld!",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# file} other {# files}} · {size} (zip file may be smaller due to compression)",
|
||||||
"share.error.visitor-limit-exceeded.title": "Bezoekerslimiet overschreden",
|
"share.error.visitor-limit-exceeded.title": "Bezoekerslimiet overschreden",
|
||||||
"share.error.visitor-limit-exceeded.description": "De bezoekerslimiet van deze share is overschreden.",
|
"share.error.visitor-limit-exceeded.description": "De bezoekerslimiet van deze share is overschreden.",
|
||||||
"share.error.removed.title": "Share was verwijderd",
|
"share.error.removed.title": "Share was verwijderd",
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export default {
|
|||||||
// /auth/reset-password
|
// /auth/reset-password
|
||||||
"resetPassword.title": "Nie pamiętasz hasła?",
|
"resetPassword.title": "Nie pamiętasz hasła?",
|
||||||
"resetPassword.description": "Wprowadź swój e-mail, aby zresetować swoje hasło.",
|
"resetPassword.description": "Wprowadź swój e-mail, aby zresetować swoje hasło.",
|
||||||
"resetPassword.notify.success": "A message with a link to reset your password has been sent if the provided email exists.",
|
"resetPassword.notify.success": "Jeśli podany adres e-mail istnieje, wiadomość z linkiem umożliwiającym zresetowanie hasła została wysłana.",
|
||||||
"resetPassword.button.back": "Powrót do strony logowania",
|
"resetPassword.button.back": "Powrót do strony logowania",
|
||||||
"resetPassword.text.resetPassword": "Resetuj hasło",
|
"resetPassword.text.resetPassword": "Resetuj hasło",
|
||||||
"resetPassword.text.enterNewPassword": "Wprowadź nowe hasło",
|
"resetPassword.text.enterNewPassword": "Wprowadź nowe hasło",
|
||||||
@@ -129,7 +129,7 @@ export default {
|
|||||||
"account.shares.table.expiry-never": "Never",
|
"account.shares.table.expiry-never": "Never",
|
||||||
"account.shares.modal.share-informations": "Informacje udziału",
|
"account.shares.modal.share-informations": "Informacje udziału",
|
||||||
"account.shares.modal.share-link": "Udostępnij link",
|
"account.shares.modal.share-link": "Udostępnij link",
|
||||||
"account.shares.modal.delete.title": "Delete share: {share}",
|
"account.shares.modal.delete.title": "Usuń udostępnianie: {share}",
|
||||||
"account.shares.modal.delete.description": "Czy na pewno chcesz usunąć to udostępnianie?",
|
"account.shares.modal.delete.description": "Czy na pewno chcesz usunąć to udostępnianie?",
|
||||||
// END /account/shares
|
// END /account/shares
|
||||||
// /account/reverseShares
|
// /account/reverseShares
|
||||||
@@ -156,7 +156,7 @@ export default {
|
|||||||
"account.reverseShares.modal.send-email": "Send email notifications",
|
"account.reverseShares.modal.send-email": "Send email notifications",
|
||||||
"account.reverseShares.modal.send-email.description": "Sends you an email notification when a share is created with this reverse share link.",
|
"account.reverseShares.modal.send-email.description": "Sends you an email notification when a share is created with this reverse share link.",
|
||||||
"account.reverseShares.modal.simplified": "Tryb uproszczony",
|
"account.reverseShares.modal.simplified": "Tryb uproszczony",
|
||||||
"account.reverseShares.modal.simplified.description": "Make it easy for the person uploading the file to share it with you. They will only be able to customize the name and description of the share.",
|
"account.reverseShares.modal.simplified.description": "",
|
||||||
"account.reverseShares.modal.public-access": "Dostęp publiczny",
|
"account.reverseShares.modal.public-access": "Dostęp publiczny",
|
||||||
"account.reverseShares.modal.public-access.description": "Make the shares created with this reverse share public. If disabled, only you and the share creator will have access to view it.",
|
"account.reverseShares.modal.public-access.description": "Make the shares created with this reverse share public. If disabled, only you and the share creator will have access to view it.",
|
||||||
"account.reverseShares.modal.max-use.label": "Limit użyć",
|
"account.reverseShares.modal.max-use.label": "Limit użyć",
|
||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "Udostępnij {shareId}",
|
"share.title": "Udostępnij {shareId}",
|
||||||
"share.description": "Spójrz, co ci udostępniłem!",
|
"share.description": "Spójrz, co ci udostępniłem!",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# file} other {# files}} · {size} (zip file may be smaller due to compression)",
|
||||||
"share.error.visitor-limit-exceeded.title": "Przekroczono limit odwiedzających",
|
"share.error.visitor-limit-exceeded.title": "Przekroczono limit odwiedzających",
|
||||||
"share.error.visitor-limit-exceeded.description": "Limit odwiedzających dla tego udziału został przekroczony.",
|
"share.error.visitor-limit-exceeded.description": "Limit odwiedzających dla tego udziału został przekroczony.",
|
||||||
"share.error.removed.title": "Udostępnianie usunięte",
|
"share.error.removed.title": "Udostępnianie usunięte",
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "Compartilhar {shareId}",
|
"share.title": "Compartilhar {shareId}",
|
||||||
"share.description": "Veja o que eu compartilhei com você!",
|
"share.description": "Veja o que eu compartilhei com você!",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# file} other {# files}} · {size} (zip file may be smaller due to compression)",
|
||||||
"share.error.visitor-limit-exceeded.title": "Limite de visitantes excedido",
|
"share.error.visitor-limit-exceeded.title": "Limite de visitantes excedido",
|
||||||
"share.error.visitor-limit-exceeded.description": "O limite de visitantes deste compartilhamento foi excedido.",
|
"share.error.visitor-limit-exceeded.description": "O limite de visitantes deste compartilhamento foi excedido.",
|
||||||
"share.error.removed.title": "Compartilhamento removido",
|
"share.error.removed.title": "Compartilhamento removido",
|
||||||
@@ -465,8 +466,8 @@ export default {
|
|||||||
"admin.config.s3.key.description": "A chave que permite a você acessar o bucket S3.",
|
"admin.config.s3.key.description": "A chave que permite a você acessar o bucket S3.",
|
||||||
"admin.config.s3.secret": "Segredo",
|
"admin.config.s3.secret": "Segredo",
|
||||||
"admin.config.s3.secret.description": "O segredo que permite a você acessar o bucket S3.",
|
"admin.config.s3.secret.description": "O segredo que permite a você acessar o bucket S3.",
|
||||||
"admin.config.s3.use-checksum": "Use checksum",
|
"admin.config.s3.use-checksum": "Usar Checksum",
|
||||||
"admin.config.s3.use-checksum.description": "Turn off for backends that do not support checksum (e.g. B2).",
|
"admin.config.s3.use-checksum.description": "Desligar para backends que não suportem checksum (por exemplo, B2).",
|
||||||
"admin.config.category.legal": "Aviso legal",
|
"admin.config.category.legal": "Aviso legal",
|
||||||
"admin.config.legal.enabled": "Habilitar avisos legais",
|
"admin.config.legal.enabled": "Habilitar avisos legais",
|
||||||
"admin.config.legal.enabled.description": "Exibir um link para imprimir e política de privacidade no rodapé.",
|
"admin.config.legal.enabled.description": "Exibir um link para imprimir e política de privacidade no rodapé.",
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "Загрузка {shareId}",
|
"share.title": "Загрузка {shareId}",
|
||||||
"share.description": "Посмотрите, чем я поделился с вами!",
|
"share.description": "Посмотрите, чем я поделился с вами!",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# file} other {# files}} · {size} (zip file may be smaller due to compression)",
|
||||||
"share.error.visitor-limit-exceeded.title": "Превышен лимит посетителей",
|
"share.error.visitor-limit-exceeded.title": "Превышен лимит посетителей",
|
||||||
"share.error.visitor-limit-exceeded.description": "Превышен лимит посетителей.",
|
"share.error.visitor-limit-exceeded.description": "Превышен лимит посетителей.",
|
||||||
"share.error.removed.title": "Загрузка удалена",
|
"share.error.removed.title": "Загрузка удалена",
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "Deli {shareId}",
|
"share.title": "Deli {shareId}",
|
||||||
"share.description": "Poglej kaj sem delil s taboj!",
|
"share.description": "Poglej kaj sem delil s taboj!",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# file} other {# files}} · {size} (zip file may be smaller due to compression)",
|
||||||
"share.error.visitor-limit-exceeded.title": "Število ogledov je prekoračena",
|
"share.error.visitor-limit-exceeded.title": "Število ogledov je prekoračena",
|
||||||
"share.error.visitor-limit-exceeded.description": "Število ogledov za to delitev je bila prekoračena.",
|
"share.error.visitor-limit-exceeded.description": "Število ogledov za to delitev je bila prekoračena.",
|
||||||
"share.error.removed.title": "Delitev je odstranjena",
|
"share.error.removed.title": "Delitev je odstranjena",
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "Deljenje {shareId}",
|
"share.title": "Deljenje {shareId}",
|
||||||
"share.description": "Pogledajte šta sam podelio sa vama!",
|
"share.description": "Pogledajte šta sam podelio sa vama!",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# file} other {# files}} · {size} (zip file may be smaller due to compression)",
|
||||||
"share.error.visitor-limit-exceeded.title": "Prekoračeno je ograničenje posetilaca",
|
"share.error.visitor-limit-exceeded.title": "Prekoračeno je ograničenje posetilaca",
|
||||||
"share.error.visitor-limit-exceeded.description": "Ograničenje posetilaca iz ovog deljenja je premašeno.",
|
"share.error.visitor-limit-exceeded.description": "Ograničenje posetilaca iz ovog deljenja je premašeno.",
|
||||||
"share.error.removed.title": "Deljenje je uklonjeno",
|
"share.error.removed.title": "Deljenje je uklonjeno",
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "Дељење {shareId}",
|
"share.title": "Дељење {shareId}",
|
||||||
"share.description": "Погледајте шта сам поделио са вама!",
|
"share.description": "Погледајте шта сам поделио са вама!",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# file} other {# files}} · {size} (zip file may be smaller due to compression)",
|
||||||
"share.error.visitor-limit-exceeded.title": "Прекорачено је ограничење посетилаца",
|
"share.error.visitor-limit-exceeded.title": "Прекорачено је ограничење посетилаца",
|
||||||
"share.error.visitor-limit-exceeded.description": "Ограничење посетилаца из овог дељења је премашено.",
|
"share.error.visitor-limit-exceeded.description": "Ограничење посетилаца из овог дељења је премашено.",
|
||||||
"share.error.removed.title": "Дељење је уклоњено",
|
"share.error.removed.title": "Дељење је уклоњено",
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "Delning {shareId}",
|
"share.title": "Delning {shareId}",
|
||||||
"share.description": "Titta vad jag har delat med dig!",
|
"share.description": "Titta vad jag har delat med dig!",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# file} other {# files}} · {size} (zip file may be smaller due to compression)",
|
||||||
"share.error.visitor-limit-exceeded.title": "Besökargränsen överskriden",
|
"share.error.visitor-limit-exceeded.title": "Besökargränsen överskriden",
|
||||||
"share.error.visitor-limit-exceeded.description": "Gränsen för antalet besökare för denna delning har överskridits.",
|
"share.error.visitor-limit-exceeded.description": "Gränsen för antalet besökare för denna delning har överskridits.",
|
||||||
"share.error.removed.title": "Delning borttagen",
|
"share.error.removed.title": "Delning borttagen",
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export default {
|
|||||||
"signin.button.submit": "เข้าสู่ระบบ",
|
"signin.button.submit": "เข้าสู่ระบบ",
|
||||||
"signIn.notify.totp-required.title": "ยืนยันตรวจสอบสิทธิ์สองปัจจัย",
|
"signIn.notify.totp-required.title": "ยืนยันตรวจสอบสิทธิ์สองปัจจัย",
|
||||||
"signIn.notify.totp-required.description": "กรุณาใส่รหัสยืนยันตัวตนสองปัจจัย",
|
"signIn.notify.totp-required.description": "กรุณาใส่รหัสยืนยันตัวตนสองปัจจัย",
|
||||||
"signIn.oauth.or": "OR",
|
"signIn.oauth.or": "หรือ",
|
||||||
"signIn.oauth.signInWith": "Sign in with",
|
"signIn.oauth.signInWith": "Sign in with",
|
||||||
"signIn.oauth.github": "GitHub",
|
"signIn.oauth.github": "GitHub",
|
||||||
"signIn.oauth.google": "Google",
|
"signIn.oauth.google": "Google",
|
||||||
@@ -485,7 +485,7 @@ export default {
|
|||||||
"error.title": "Error",
|
"error.title": "Error",
|
||||||
"error.description": "Oops!",
|
"error.description": "Oops!",
|
||||||
"error.button.back": "Go back",
|
"error.button.back": "Go back",
|
||||||
"error.msg.default": "Something went wrong.",
|
"error.msg.default": "มีบางอย่างผิดพลาด",
|
||||||
"error.msg.access_denied": "You canceled the authentication process, please try again.",
|
"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.expired_token": "The authentication process took too long, please try again.",
|
||||||
"error.msg.invalid_token": "Internal Error",
|
"error.msg.invalid_token": "Internal Error",
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "{shareId} Paylaşımı",
|
"share.title": "{shareId} Paylaşımı",
|
||||||
"share.description": "Sizinle paylaştıklarımı bir göz atın!",
|
"share.description": "Sizinle paylaştıklarımı bir göz atın!",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# dosya} other {# dosya}} · {size} (sıkıştırma nedeniyle zip dosyası daha küçük olabilir)",
|
||||||
"share.error.visitor-limit-exceeded.title": "Ziyaretçi limiti aşıldı",
|
"share.error.visitor-limit-exceeded.title": "Ziyaretçi limiti aşıldı",
|
||||||
"share.error.visitor-limit-exceeded.description": "Bu paylaşımın ziyaretçi limiti aşıldı.",
|
"share.error.visitor-limit-exceeded.description": "Bu paylaşımın ziyaretçi limiti aşıldı.",
|
||||||
"share.error.removed.title": "Paylaşım kaldırıldı",
|
"share.error.removed.title": "Paylaşım kaldırıldı",
|
||||||
@@ -465,8 +466,8 @@ export default {
|
|||||||
"admin.config.s3.key.description": "S3 kovasına erişmenizi sağlayan anahtar.",
|
"admin.config.s3.key.description": "S3 kovasına erişmenizi sağlayan anahtar.",
|
||||||
"admin.config.s3.secret": "Gizli",
|
"admin.config.s3.secret": "Gizli",
|
||||||
"admin.config.s3.secret.description": "S3 kovasına erişmenizi sağlayan gizli anahtar.",
|
"admin.config.s3.secret.description": "S3 kovasına erişmenizi sağlayan gizli anahtar.",
|
||||||
"admin.config.s3.use-checksum": "Use checksum",
|
"admin.config.s3.use-checksum": "Doğrulama kullan",
|
||||||
"admin.config.s3.use-checksum.description": "Turn off for backends that do not support checksum (e.g. B2).",
|
"admin.config.s3.use-checksum.description": "Doğrulama desteklemeyen arka uçlar için kapatın (ör. B2).",
|
||||||
"admin.config.category.legal": "Yasal",
|
"admin.config.category.legal": "Yasal",
|
||||||
"admin.config.legal.enabled": "Yasal Bildirimleri Etkinleştir",
|
"admin.config.legal.enabled": "Yasal Bildirimleri Etkinleştir",
|
||||||
"admin.config.legal.enabled.description": "Alt bilgi kısmında künyeye ve gizlilik politikasına bir bağlantı gösterilip gösterilmeyeceğini belirtir.",
|
"admin.config.legal.enabled.description": "Alt bilgi kısmında künyeye ve gizlilik politikasına bir bağlantı gösterilip gösterilmeyeceğini belirtir.",
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "Завантаження {shareId}",
|
"share.title": "Завантаження {shareId}",
|
||||||
"share.description": "Подивіться, чим я поділився з вами!",
|
"share.description": "Подивіться, чим я поділився з вами!",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# file} other {# files}} · {size} (zip file may be smaller due to compression)",
|
||||||
"share.error.visitor-limit-exceeded.title": "Перевищено ліміт відвідувачів",
|
"share.error.visitor-limit-exceeded.title": "Перевищено ліміт відвідувачів",
|
||||||
"share.error.visitor-limit-exceeded.description": "Перевищено ліміт відвідувачів.",
|
"share.error.visitor-limit-exceeded.description": "Перевищено ліміт відвідувачів.",
|
||||||
"share.error.removed.title": "Завантаження видалено",
|
"share.error.removed.title": "Завантаження видалено",
|
||||||
@@ -465,8 +466,8 @@ export default {
|
|||||||
"admin.config.s3.key.description": "Ключ, який дозволяє отримати доступ до S3 бакету.",
|
"admin.config.s3.key.description": "Ключ, який дозволяє отримати доступ до S3 бакету.",
|
||||||
"admin.config.s3.secret": "Секрет",
|
"admin.config.s3.secret": "Секрет",
|
||||||
"admin.config.s3.secret.description": "Секрет, який дозволяє отримати доступ до S3 бакету.",
|
"admin.config.s3.secret.description": "Секрет, який дозволяє отримати доступ до S3 бакету.",
|
||||||
"admin.config.s3.use-checksum": "Use checksum",
|
"admin.config.s3.use-checksum": "Використовувати контрольну суму",
|
||||||
"admin.config.s3.use-checksum.description": "Turn off for backends that do not support checksum (e.g. B2).",
|
"admin.config.s3.use-checksum.description": "Вимкніть для непідтримуваних контрольних сум (напр. B2).",
|
||||||
"admin.config.category.legal": "Юридична інформація",
|
"admin.config.category.legal": "Юридична інформація",
|
||||||
"admin.config.legal.enabled": "Увімкнути правові сповіщення",
|
"admin.config.legal.enabled": "Увімкнути правові сповіщення",
|
||||||
"admin.config.legal.enabled.description": "Чи відображати посилання на вихідні дані та політику конфіденційності у нижньому колонтитулі.",
|
"admin.config.legal.enabled.description": "Чи відображати посилання на вихідні дані та політику конфіденційності у нижньому колонтитулі.",
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "Share {shareId}",
|
"share.title": "Share {shareId}",
|
||||||
"share.description": "Look what I've shared with you!",
|
"share.description": "Look what I've shared with you!",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# file} other {# files}} · {size} (zip file may be smaller due to compression)",
|
||||||
"share.error.visitor-limit-exceeded.title": "Visitor limit exceeded",
|
"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.visitor-limit-exceeded.description": "The visitor limit from this share has been exceeded.",
|
||||||
"share.error.removed.title": "Share removed",
|
"share.error.removed.title": "Share removed",
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "共享 {shareId}",
|
"share.title": "共享 {shareId}",
|
||||||
"share.description": "瞧瞧我给你共享了些什么!",
|
"share.description": "瞧瞧我给你共享了些什么!",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# file} other {# files}} · {size} (zip file may be smaller due to compression)",
|
||||||
"share.error.visitor-limit-exceeded.title": "访问次数达到上限",
|
"share.error.visitor-limit-exceeded.title": "访问次数达到上限",
|
||||||
"share.error.visitor-limit-exceeded.description": "访问次数达到上限",
|
"share.error.visitor-limit-exceeded.description": "访问次数达到上限",
|
||||||
"share.error.removed.title": "共享已删除",
|
"share.error.removed.title": "共享已删除",
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "分享 {shareId}",
|
"share.title": "分享 {shareId}",
|
||||||
"share.description": "瞧瞧我給您分享了些什麽!",
|
"share.description": "瞧瞧我給您分享了些什麽!",
|
||||||
|
"share.fileCount": "{count, plural, =1 {# file} other {# files}} · {size} (zip file may be smaller due to compression)",
|
||||||
"share.error.visitor-limit-exceeded.title": "查看次數達到上限",
|
"share.error.visitor-limit-exceeded.title": "查看次數達到上限",
|
||||||
"share.error.visitor-limit-exceeded.description": "查看次數達到上限",
|
"share.error.visitor-limit-exceeded.description": "查看次數達到上限",
|
||||||
"share.error.removed.title": "分享已刪除",
|
"share.error.removed.title": "分享已刪除",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Box, Group, Text, Title } from "@mantine/core";
|
|||||||
import { useModals } from "@mantine/modals";
|
import { useModals } from "@mantine/modals";
|
||||||
import { GetServerSidePropsContext } from "next";
|
import { GetServerSidePropsContext } from "next";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { FormattedMessage } from "react-intl";
|
||||||
import Meta from "../../../components/Meta";
|
import Meta from "../../../components/Meta";
|
||||||
import DownloadAllButton from "../../../components/share/DownloadAllButton";
|
import DownloadAllButton from "../../../components/share/DownloadAllButton";
|
||||||
import FileList from "../../../components/share/FileList";
|
import FileList from "../../../components/share/FileList";
|
||||||
@@ -11,6 +12,7 @@ import useTranslate from "../../../hooks/useTranslate.hook";
|
|||||||
import shareService from "../../../services/share.service";
|
import shareService from "../../../services/share.service";
|
||||||
import { Share as ShareType } from "../../../types/share.type";
|
import { Share as ShareType } from "../../../types/share.type";
|
||||||
import toast from "../../../utils/toast.util";
|
import toast from "../../../utils/toast.util";
|
||||||
|
import { byteToHumanSizeString } from "../../../utils/fileSize.util";
|
||||||
|
|
||||||
export function getServerSideProps(context: GetServerSidePropsContext) {
|
export function getServerSideProps(context: GetServerSidePropsContext) {
|
||||||
return {
|
return {
|
||||||
@@ -107,7 +109,25 @@ const Share = ({ shareId }: { shareId: string }) => {
|
|||||||
<Box style={{ maxWidth: "70%" }}>
|
<Box style={{ maxWidth: "70%" }}>
|
||||||
<Title order={3}>{share?.name || share?.id}</Title>
|
<Title order={3}>{share?.name || share?.id}</Title>
|
||||||
<Text size="sm">{share?.description}</Text>
|
<Text size="sm">{share?.description}</Text>
|
||||||
|
{share?.files?.length > 0 && (
|
||||||
|
<Text size="sm" color="dimmed" mt={5}>
|
||||||
|
<FormattedMessage
|
||||||
|
id="share.fileCount"
|
||||||
|
values={{
|
||||||
|
count: share?.files?.length || 0,
|
||||||
|
size: byteToHumanSizeString(
|
||||||
|
share?.files?.reduce(
|
||||||
|
(total: number, file: { size: string }) =>
|
||||||
|
total + parseInt(file.size),
|
||||||
|
0,
|
||||||
|
) || 0,
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{share?.files.length > 1 && <DownloadAllButton shareId={shareId} />}
|
{share?.files.length > 1 && <DownloadAllButton shareId={shareId} />}
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "pingvin-share",
|
"name": "pingvin-share",
|
||||||
"version": "1.11.1",
|
"version": "1.12.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "pingvin-share",
|
"name": "pingvin-share",
|
||||||
"version": "1.11.1",
|
"version": "1.12.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"conventional-changelog-cli": "^3.0.0"
|
"conventional-changelog-cli": "^3.0.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pingvin-share",
|
"name": "pingvin-share",
|
||||||
"version": "1.11.1",
|
"version": "1.12.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"format": "cd frontend && npm run format && cd ../backend && npm run format",
|
"format": "cd frontend && npm run format && cd ../backend && npm run format",
|
||||||
"lint": "cd frontend && npm run lint && cd ../backend && npm run lint",
|
"lint": "cd frontend && npm run lint && cd ../backend && npm run lint",
|
||||||
@@ -11,5 +11,9 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"conventional-changelog-cli": "^3.0.0"
|
"conventional-changelog-cli": "^3.0.0"
|
||||||
|
},
|
||||||
|
"prettier": {
|
||||||
|
"singleQuote": false,
|
||||||
|
"trailingComma": "all"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user