Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b39adfd03 | ||
|
|
d9cfe697d6 | ||
|
|
67a0fc6ea5 | ||
|
|
b13a81a88c | ||
|
|
97dc3ecfdd | ||
|
|
d00d52baa9 | ||
|
|
4c8848a2d9 | ||
|
|
3c8500008d | ||
|
|
325122b802 | ||
|
|
7dc2e56fee | ||
|
|
8b3e28bac8 | ||
|
|
347026b6d3 | ||
|
|
5a204d38a4 | ||
|
|
2eeb858f36 | ||
|
|
67faa860da | ||
|
|
beca26871d | ||
|
|
15d1756a4e | ||
|
|
be202d3d41 | ||
|
|
f0e785b1a2 | ||
|
|
92e1e82e09 |
27
CHANGELOG.md
27
CHANGELOG.md
@@ -1,3 +1,30 @@
|
|||||||
|
## [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)
|
## [1.0.3](https://github.com/stonith404/pingvin-share/compare/v1.0.2...v1.0.3) (2024-09-03)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
15
Caddyfile
15
Caddyfile
@@ -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}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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/prisma ./prisma
|
||||||
COPY --from=backend-builder /opt/app/package.json ./
|
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
|
COPY ./scripts/docker-entrypoint.sh /opt/app/docker-entrypoint.sh
|
||||||
|
|
||||||
WORKDIR /opt/app
|
WORKDIR /opt/app
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -13,6 +13,8 @@ Pingvin Share is a self-hosted file sharing platform and an alternative for WeTr
|
|||||||
- Set an expiration date for shares
|
- Set an expiration date for shares
|
||||||
- Secure shares with visitor limits and passwords
|
- Secure shares with visitor limits and passwords
|
||||||
- Email recipients
|
- Email recipients
|
||||||
|
- Reverse shares
|
||||||
|
- OIDC and LDAP authentication
|
||||||
- Integration with ClamAV for security scans
|
- Integration with ClamAV for security scans
|
||||||
|
|
||||||
## 🐧 Get to know Pingvin Share
|
## 🐧 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 🐧!
|
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
|
## 📚 Documentation
|
||||||
|
|
||||||
For more installation options and advanced configurations, please refer to the [documentation](https://stonith404.github.io/pingvin-share).
|
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
2045
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.0.3",
|
"version": "1.1.1",
|
||||||
"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",
|
||||||
@@ -14,23 +14,23 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nestjs/cache-manager": "^2.2.2",
|
"@nestjs/cache-manager": "^2.2.2",
|
||||||
"@nestjs/common": "^10.3.9",
|
"@nestjs/common": "^10.4.3",
|
||||||
"@nestjs/config": "^3.2.2",
|
"@nestjs/config": "^3.2.3",
|
||||||
"@nestjs/core": "^10.3.9",
|
"@nestjs/core": "^10.4.3",
|
||||||
"@nestjs/jwt": "^10.2.0",
|
"@nestjs/jwt": "^10.2.0",
|
||||||
"@nestjs/passport": "^10.0.3",
|
"@nestjs/passport": "^10.0.3",
|
||||||
"@nestjs/platform-express": "^10.3.9",
|
"@nestjs/platform-express": "^10.4.3",
|
||||||
"@nestjs/schedule": "^4.0.2",
|
"@nestjs/schedule": "^4.1.1",
|
||||||
"@nestjs/swagger": "^7.3.1",
|
"@nestjs/swagger": "^7.4.2",
|
||||||
"@nestjs/throttler": "^5.2.0",
|
"@nestjs/throttler": "^6.2.1",
|
||||||
"@prisma/client": "^5.16.1",
|
"@prisma/client": "^5.19.1",
|
||||||
"@types/jmespath": "^0.15.2",
|
"@types/jmespath": "^0.15.2",
|
||||||
"@types/ldapjs": "^3.0.6",
|
"@types/ldapjs": "^3.0.6",
|
||||||
"archiver": "^7.0.1",
|
"archiver": "^7.0.1",
|
||||||
"argon2": "^0.40.3",
|
"argon2": "^0.41.1",
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.3",
|
||||||
"cache-manager": "^5.6.1",
|
"cache-manager": "^5.7.6",
|
||||||
"clamscan": "^2.2.1",
|
"clamscan": "^2.3.1",
|
||||||
"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",
|
||||||
@@ -40,48 +40,48 @@
|
|||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"nanoid": "^3.3.7",
|
"nanoid": "^3.3.7",
|
||||||
"nodemailer": "^6.9.14",
|
"nodemailer": "^6.9.15",
|
||||||
"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",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
"qrcode-svg": "^1.1.0",
|
"qrcode-svg": "^1.1.0",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"rimraf": "^5.0.7",
|
"rimraf": "^6.0.1",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
"sharp": "^0.33.4",
|
"sharp": "^0.33.5",
|
||||||
"ts-node": "^10.9.2"
|
"ts-node": "^10.9.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nestjs/cli": "^10.3.2",
|
"@nestjs/cli": "^10.4.5",
|
||||||
"@nestjs/schematics": "^10.1.1",
|
"@nestjs/schematics": "^10.1.4",
|
||||||
"@nestjs/testing": "^10.3.9",
|
"@nestjs/testing": "^10.4.3",
|
||||||
"@types/archiver": "^6.0.2",
|
"@types/archiver": "^6.0.2",
|
||||||
"@types/clamscan": "^2.0.8",
|
"@types/clamscan": "^2.0.8",
|
||||||
"@types/cookie-parser": "^1.4.7",
|
"@types/cookie-parser": "^1.4.7",
|
||||||
"@types/cron": "^2.0.1",
|
"@types/cron": "^2.4.0",
|
||||||
"@types/express": "^4.17.21",
|
"@types/express": "^4.17.21",
|
||||||
"@types/mime-types": "^2.1.4",
|
"@types/mime-types": "^2.1.4",
|
||||||
"@types/multer": "^1.4.11",
|
"@types/multer": "^1.4.12",
|
||||||
"@types/node": "^20.14.9",
|
"@types/node": "^22.5.5",
|
||||||
"@types/nodemailer": "^6.4.15",
|
"@types/nodemailer": "^6.4.16",
|
||||||
"@types/passport-jwt": "^4.0.1",
|
"@types/passport-jwt": "^4.0.1",
|
||||||
"@types/qrcode-svg": "^1.1.4",
|
"@types/qrcode-svg": "^1.1.5",
|
||||||
"@types/sharp": "^0.31.1",
|
"@types/sharp": "^0.32.0",
|
||||||
"@types/supertest": "^6.0.2",
|
"@types/supertest": "^6.0.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.14.1",
|
"@typescript-eslint/eslint-plugin": "^8.6.0",
|
||||||
"@typescript-eslint/parser": "^7.14.1",
|
"@typescript-eslint/parser": "^8.6.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^9.10.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.2.1",
|
||||||
"newman": "^6.1.3",
|
"newman": "^6.2.1",
|
||||||
"prettier": "^3.3.2",
|
"prettier": "^3.3.3",
|
||||||
"prisma": "^5.16.1",
|
"prisma": "^5.19.1",
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "^0.5.21",
|
||||||
"ts-loader": "^9.5.1",
|
"ts-loader": "^9.5.1",
|
||||||
"tsconfig-paths": "4.2.0",
|
"tsconfig-paths": "4.2.0",
|
||||||
"typescript": "^5.5.2",
|
"typescript": "^5.6.2",
|
||||||
"wait-on": "^7.2.0"
|
"wait-on": "^8.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,14 +17,15 @@ export class EmailService {
|
|||||||
if (!this.config.get("smtp.enabled"))
|
if (!this.config.get("smtp.enabled"))
|
||||||
throw new InternalServerErrorException("SMTP is disabled");
|
throw new InternalServerErrorException("SMTP is disabled");
|
||||||
|
|
||||||
|
const username = this.config.get("smtp.username");
|
||||||
|
const password = this.config.get("smtp.password");
|
||||||
|
|
||||||
return nodemailer.createTransport({
|
return nodemailer.createTransport({
|
||||||
host: this.config.get("smtp.host"),
|
host: this.config.get("smtp.host"),
|
||||||
port: this.config.get("smtp.port"),
|
port: this.config.get("smtp.port"),
|
||||||
secure: this.config.get("smtp.port") == 465,
|
secure: this.config.get("smtp.port") == 465,
|
||||||
auth: {
|
auth:
|
||||||
user: this.config.get("smtp.username"),
|
username || password ? { user: username, pass: password } : undefined,
|
||||||
pass: this.config.get("smtp.password"),
|
|
||||||
},
|
|
||||||
tls: {
|
tls: {
|
||||||
rejectUnauthorized: !this.config.get(
|
rejectUnauthorized: !this.config.get(
|
||||||
"smtp.allowUnauthorizedCertificates",
|
"smtp.allowUnauthorizedCertificates",
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export class OAuthService {
|
|||||||
await this.updateIsAdmin(user);
|
await this.updateIsAdmin(user);
|
||||||
const updatedUser = await this.prisma.user.findFirst({
|
const updatedUser = await this.prisma.user.findFirst({
|
||||||
where: {
|
where: {
|
||||||
email: user.email,
|
id: oauthUser.userId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
this.logger.log(`Successful login for user ${user.email} from IP ${ip}`);
|
this.logger.log(`Successful login for user ${user.email} from IP ${ip}`);
|
||||||
|
|||||||
@@ -4,15 +4,11 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
|
environment:
|
||||||
|
- TRUST_PROXY=false # Set to true if a reverse proxy is in front of the container
|
||||||
volumes:
|
volumes:
|
||||||
- "./data:/opt/app/backend/data"
|
- "./data:/opt/app/backend/data"
|
||||||
- "./data/images:/opt/app/frontend/public/img"
|
- "./data/images:/opt/app/frontend/public/img"
|
||||||
# Optional: If you add ClamAV, uncomment the following to have ClamAV start first.
|
|
||||||
# depends_on:
|
# To add ClamAV, to scan your shares for malicious files,
|
||||||
# clamav:
|
# see https://stonith404.github.io/pingvin-share/setup/integrations/#clamav-docker-only
|
||||||
# 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
|
|
||||||
|
|||||||
@@ -4,13 +4,35 @@ id: configuration
|
|||||||
|
|
||||||
# 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:
|
For installation specific configuration, you can use environment variables. The following variables are available:
|
||||||
|
|
||||||
##### Backend
|
#### Backend
|
||||||
|
|
||||||
| Variable | Default Value | Description |
|
| Variable | Default Value | Description |
|
||||||
| ---------------- | -------------------------------------------------- | -------------------------------------- |
|
| ---------------- | -------------------------------------------------- | -------------------------------------- |
|
||||||
@@ -20,9 +42,15 @@ For installation specific configuration, you can use environment variables. The
|
|||||||
| `CLAMAV_HOST` | `127.0.0.1` | The IP address of the ClamAV server. |
|
| `CLAMAV_HOST` | `127.0.0.1` | The IP address of the ClamAV server. |
|
||||||
| `CLAMAV_PORT` | `3310` | The port number of the ClamAV server. |
|
| `CLAMAV_PORT` | `3310` | The port number of the ClamAV server. |
|
||||||
|
|
||||||
##### Frontend
|
#### Frontend
|
||||||
|
|
||||||
| Variable | Default Value | Description |
|
| Variable | Default Value | Description |
|
||||||
| --------- | ----------------------- | ---------------------------------------- |
|
| --------- | ----------------------- | ---------------------------------------- |
|
||||||
| `PORT` | `3000` | The port on which the frontend listens. |
|
| `PORT` | `3000` | The port on which the frontend listens. |
|
||||||
| `API_URL` | `http://localhost:8080` | The URL of the backend for the frontend. |
|
| `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. |
|
||||||
|
|||||||
@@ -40,6 +40,6 @@ API_URL=http://localhost:8080 # Set the URL of the backend, default: http://loca
|
|||||||
pm2 start --name="pingvin-share-frontend" .next/standalone/server.js
|
pm2 start --name="pingvin-share-frontend" .next/standalone/server.js
|
||||||
```
|
```
|
||||||
|
|
||||||
**Uploading Large Files**: By default, Pingvin Share uses a built-in reverse proxy to reduce the installation steps. However, this reverse proxy is not optimized for uploading large files. If you wish to upload larger files, you can either use the Docker installation or set up your own reverse proxy. An example configuration for Caddy can be found in `./Caddyfile`.
|
**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 🐧!
|
The website is now listening on `http://localhost:3000`, have fun with Pingvin Share 🐧!
|
||||||
|
|||||||
@@ -4,11 +4,29 @@ id: integrations
|
|||||||
|
|
||||||
# Integrations
|
# Integrations
|
||||||
|
|
||||||
#### ClamAV (Docker only)
|
#### ClamAV
|
||||||
|
|
||||||
|
> **_NOTE:_** Currently ClamAV is only available in the Docker installation.
|
||||||
|
|
||||||
ClamAV is used to scan shares for malicious files and remove them if found.
|
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.
|
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.
|
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"
|
3. The Pingvin Share logs should now log "ClamAV is active"
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const config: Config = {
|
|||||||
tagline:
|
tagline:
|
||||||
"Pingvin Share is self-hosted file sharing platform and an alternative for WeTransfer.",
|
"Pingvin Share is self-hosted file sharing platform and an alternative for WeTransfer.",
|
||||||
favicon: "img/pingvinshare.svg",
|
favicon: "img/pingvinshare.svg",
|
||||||
|
|
||||||
url: "https://stonith404.github.io",
|
url: "https://stonith404.github.io",
|
||||||
baseUrl: "/pingvin-share/",
|
baseUrl: "/pingvin-share/",
|
||||||
organizationName: "stonith404",
|
organizationName: "stonith404",
|
||||||
@@ -37,7 +37,7 @@ const config: Config = {
|
|||||||
|
|
||||||
themeConfig: {
|
themeConfig: {
|
||||||
image: "img/pingvinshare.svg",
|
image: "img/pingvinshare.svg",
|
||||||
colorMode:{
|
colorMode: {
|
||||||
respectPrefersColorScheme: true,
|
respectPrefersColorScheme: true,
|
||||||
},
|
},
|
||||||
navbar: {
|
navbar: {
|
||||||
|
|||||||
828
docs/package-lock.json
generated
828
docs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@
|
|||||||
"start": "docusaurus start",
|
"start": "docusaurus start",
|
||||||
"build": "docusaurus build",
|
"build": "docusaurus build",
|
||||||
"swizzle": "docusaurus swizzle",
|
"swizzle": "docusaurus swizzle",
|
||||||
"deploy": "docusaurus deploy",
|
"deploy": "GIT_USER=stonith404 docusaurus deploy",
|
||||||
"clear": "docusaurus clear",
|
"clear": "docusaurus clear",
|
||||||
"serve": "docusaurus serve",
|
"serve": "docusaurus serve",
|
||||||
"write-translations": "docusaurus write-translations",
|
"write-translations": "docusaurus write-translations",
|
||||||
@@ -15,19 +15,19 @@
|
|||||||
"typecheck": "tsc"
|
"typecheck": "tsc"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "3.4.0",
|
"@docusaurus/core": "3.5.2",
|
||||||
"@docusaurus/preset-classic": "3.4.0",
|
"@docusaurus/preset-classic": "3.5.2",
|
||||||
"@mdx-js/react": "^3.0.0",
|
"@mdx-js/react": "^3.0.1",
|
||||||
"clsx": "^2.0.0",
|
"clsx": "^2.1.1",
|
||||||
"prism-react-renderer": "^2.3.0",
|
"prism-react-renderer": "^2.4.0",
|
||||||
"react": "^18.0.0",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.0.0"
|
"react-dom": "^18.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@docusaurus/module-type-aliases": "3.4.0",
|
"@docusaurus/module-type-aliases": "3.5.2",
|
||||||
"@docusaurus/tsconfig": "3.4.0",
|
"@docusaurus/tsconfig": "3.5.2",
|
||||||
"@docusaurus/types": "3.4.0",
|
"@docusaurus/types": "3.5.2",
|
||||||
"typescript": "~5.2.2"
|
"typescript": "~5.6.2"
|
||||||
},
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
"production": [
|
"production": [
|
||||||
|
|||||||
2
frontend/next-env.d.ts
vendored
2
frontend/next-env.d.ts
vendored
@@ -2,4 +2,4 @@
|
|||||||
/// <reference types="next/image-types/global" />
|
/// <reference types="next/image-types/global" />
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
// 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.
|
||||||
|
|||||||
3091
frontend/package-lock.json
generated
3091
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pingvin-share-frontend",
|
"name": "pingvin-share-frontend",
|
||||||
"version": "1.0.3",
|
"version": "1.1.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
"format": "prettier --end-of-line=auto --write \"src/**/*.ts*\""
|
"format": "prettier --end-of-line=auto --write \"src/**/*.ts*\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.11.4",
|
"@emotion/react": "^11.13.3",
|
||||||
"@emotion/server": "^11.11.0",
|
"@emotion/server": "^11.11.0",
|
||||||
"@mantine/core": "^6.0.21",
|
"@mantine/core": "^6.0.21",
|
||||||
"@mantine/dropzone": "^6.0.21",
|
"@mantine/dropzone": "^6.0.21",
|
||||||
@@ -18,37 +18,37 @@
|
|||||||
"@mantine/modals": "^6.0.21",
|
"@mantine/modals": "^6.0.21",
|
||||||
"@mantine/next": "^6.0.21",
|
"@mantine/next": "^6.0.21",
|
||||||
"@mantine/notifications": "^6.0.21",
|
"@mantine/notifications": "^6.0.21",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.7",
|
||||||
"cookies-next": "^2.1.2",
|
"cookies-next": "^4.2.1",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"jose": "^4.15.5",
|
"jose": "^5.9.2",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^4.0.0",
|
||||||
"markdown-to-jsx": "^7.4.7",
|
"markdown-to-jsx": "^7.5.0",
|
||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"next": "^14.2.3",
|
"next": "^14.2.12",
|
||||||
"next-http-proxy-middleware": "^1.2.6",
|
"next-http-proxy-middleware": "^1.2.6",
|
||||||
"next-pwa": "^5.6.0",
|
"next-pwa": "^5.6.0",
|
||||||
"p-limit": "^4.0.0",
|
"p-limit": "^6.1.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-icons": "^4.12.0",
|
"react-icons": "^5.3.0",
|
||||||
"react-intl": "^6.6.8",
|
"react-intl": "^6.6.8",
|
||||||
"sharp": "^0.33.4",
|
"sharp": "^0.33.5",
|
||||||
"yup": "^1.4.0"
|
"yup": "^1.4.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/mime-types": "^2.1.4",
|
"@types/mime-types": "^2.1.4",
|
||||||
"@types/node": "20.12.12",
|
"@types/node": "22.5.5",
|
||||||
"@types/react": "18.3.2",
|
"@types/react": "18.3.7",
|
||||||
"@types/react-dom": "18.3.0",
|
"@types/react-dom": "18.3.0",
|
||||||
"@typescript-eslint/parser": "^7.10.0",
|
"@typescript-eslint/parser": "^8.6.0",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.7",
|
||||||
"eslint": "8.57.0",
|
"eslint": "8.57.0",
|
||||||
"eslint-config-next": "^13.5.6",
|
"eslint-config-next": "^14.2.12",
|
||||||
"eslint-config-prettier": "^8.10.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.3.3",
|
||||||
"tar": "^6.2.1",
|
"tar": "^7.4.3",
|
||||||
"typescript": "^5.4.5"
|
"typescript": "^5.6.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
Container,
|
Container,
|
||||||
createStyles,
|
createStyles,
|
||||||
Group,
|
Group,
|
||||||
|
Loader,
|
||||||
Paper,
|
Paper,
|
||||||
PasswordInput,
|
PasswordInput,
|
||||||
Stack,
|
Stack,
|
||||||
@@ -15,7 +16,7 @@ import { useForm, yupResolver } from "@mantine/form";
|
|||||||
import { showNotification } from "@mantine/notifications";
|
import { showNotification } from "@mantine/notifications";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import React from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { TbInfoCircle } from "react-icons/tb";
|
import { TbInfoCircle } from "react-icons/tb";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
@@ -24,8 +25,8 @@ import useUser from "../../hooks/user.hook";
|
|||||||
import useTranslate from "../../hooks/useTranslate.hook";
|
import useTranslate from "../../hooks/useTranslate.hook";
|
||||||
import authService from "../../services/auth.service";
|
import authService from "../../services/auth.service";
|
||||||
import { getOAuthIcon, getOAuthUrl } from "../../utils/oauth.util";
|
import { getOAuthIcon, getOAuthUrl } from "../../utils/oauth.util";
|
||||||
import toast from "../../utils/toast.util";
|
|
||||||
import { safeRedirectPath } from "../../utils/router.util";
|
import { safeRedirectPath } from "../../utils/router.util";
|
||||||
|
import toast from "../../utils/toast.util";
|
||||||
|
|
||||||
const useStyles = createStyles((theme) => ({
|
const useStyles = createStyles((theme) => ({
|
||||||
signInWith: {
|
signInWith: {
|
||||||
@@ -74,10 +75,14 @@ const SignInForm = ({ redirectPath }: { redirectPath: string }) => {
|
|||||||
const { refreshUser } = useUser();
|
const { refreshUser } = useUser();
|
||||||
const { classes } = useStyles();
|
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({
|
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
|
password: yup
|
||||||
.string()
|
.string()
|
||||||
.min(8, t("common.error.too-short", { length: 8 }))
|
.min(8, t("common.error.too-short", { length: 8 }))
|
||||||
@@ -118,15 +123,36 @@ const SignInForm = ({ redirectPath }: { redirectPath: string }) => {
|
|||||||
.catch(toast.axiosError);
|
.catch(toast.axiosError);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAvailableOAuth = async () => {
|
useEffect(() => {
|
||||||
const oauth = await authService.getAvailableOAuth();
|
authService
|
||||||
setOAuth(oauth.data);
|
.getAvailableOAuth()
|
||||||
};
|
.then((providers) => {
|
||||||
|
setOauthProviders(providers.data);
|
||||||
React.useEffect(() => {
|
if (
|
||||||
getAvailableOAuth().catch(toast.axiosError);
|
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 (
|
return (
|
||||||
<Container size={420} my={40}>
|
<Container size={420} my={40}>
|
||||||
<Title order={2} align="center" weight={900}>
|
<Title order={2} align="center" weight={900}>
|
||||||
@@ -148,8 +174,16 @@ const SignInForm = ({ redirectPath }: { redirectPath: string }) => {
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<TextInput
|
<TextInput
|
||||||
label={t("signin.input.email-or-username")}
|
label={
|
||||||
placeholder={t("signin.input.email-or-username.placeholder")}
|
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")}
|
{...form.getInputProps("emailOrUsername")}
|
||||||
/>
|
/>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
@@ -170,7 +204,7 @@ const SignInForm = ({ redirectPath }: { redirectPath: string }) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
)}
|
)}
|
||||||
{oauth.length > 0 && (
|
{oauthProviders.length > 0 && (
|
||||||
<Stack mt={config.get("oauth.disablePassword") ? undefined : "xl"}>
|
<Stack mt={config.get("oauth.disablePassword") ? undefined : "xl"}>
|
||||||
{config.get("oauth.disablePassword") ? (
|
{config.get("oauth.disablePassword") ? (
|
||||||
<Group align="center" className={classes.signInWith}>
|
<Group align="center" className={classes.signInWith}>
|
||||||
@@ -182,7 +216,7 @@ const SignInForm = ({ redirectPath }: { redirectPath: string }) => {
|
|||||||
</Group>
|
</Group>
|
||||||
)}
|
)}
|
||||||
<Group position="center">
|
<Group position="center">
|
||||||
{oauth.map((provider) => (
|
{oauthProviders.map((provider) => (
|
||||||
<Button
|
<Button
|
||||||
key={provider}
|
key={provider}
|
||||||
component="a"
|
component="a"
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export default {
|
|||||||
"signIn.notify.totp-required.title": "Απαιτείται έλεγχος ταυτότητας δύο παραγόντων.",
|
"signIn.notify.totp-required.title": "Απαιτείται έλεγχος ταυτότητας δύο παραγόντων.",
|
||||||
"signIn.notify.totp-required.description": "Παρακαλώ εισάγετε τον κωδικό 2FA.",
|
"signIn.notify.totp-required.description": "Παρακαλώ εισάγετε τον κωδικό 2FA.",
|
||||||
"signIn.oauth.or": "Ή",
|
"signIn.oauth.or": "Ή",
|
||||||
"signIn.oauth.signInWith": "Sign in with",
|
"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",
|
||||||
@@ -152,7 +152,7 @@ export default {
|
|||||||
"account.reverseShares.modal.max-size.label": "Μέγιστο μέγεθος κοινοποίησης",
|
"account.reverseShares.modal.max-size.label": "Μέγιστο μέγεθος κοινοποίησης",
|
||||||
"account.reverseShares.modal.send-email": "Αποστολή ειδοποιήσεων με email",
|
"account.reverseShares.modal.send-email": "Αποστολή ειδοποιήσεων με email",
|
||||||
"account.reverseShares.modal.send-email.description": "Στείλτε μια ειδοποίηση μέσω ηλεκτρονικού ταχυδρομείου όταν δημιουργείται ένας διαμοιρασμός με αυτόν τον σύνδεσμο ανάστροφης κοινοποίησης.",
|
"account.reverseShares.modal.send-email.description": "Στείλτε μια ειδοποίηση μέσω ηλεκτρονικού ταχυδρομείου όταν δημιουργείται ένας διαμοιρασμός με αυτόν τον σύνδεσμο ανάστροφης κοινοποίησης.",
|
||||||
"account.reverseShares.modal.simplified": "Simple mode",
|
"account.reverseShares.modal.simplified": "Απλή λειτουργία",
|
||||||
"account.reverseShares.modal.simplified.description": "Make it easy for the person uploading the file to share it with you. They will be able to customize only the name and description of the share.",
|
"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": "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": "Make the created shares with this reverse share public. If disabled, only you and the creator of the share can view it.",
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ export default {
|
|||||||
"signIn.oauth.microsoft": "Microsoft",
|
"signIn.oauth.microsoft": "Microsoft",
|
||||||
"signIn.oauth.discord": "Discord",
|
"signIn.oauth.discord": "Discord",
|
||||||
"signIn.oauth.oidc": "OpenID",
|
"signIn.oauth.oidc": "OpenID",
|
||||||
|
"signIn.error.invalid-username": "Invalid username",
|
||||||
|
|
||||||
// END /auth/signin
|
// END /auth/signin
|
||||||
|
|
||||||
@@ -631,6 +632,7 @@ export default {
|
|||||||
"common.text.link": "Link",
|
"common.text.link": "Link",
|
||||||
"common.text.navigate-to-link": "Go to the link",
|
"common.text.navigate-to-link": "Go to the link",
|
||||||
"common.text.or": "or",
|
"common.text.or": "or",
|
||||||
|
"common.text.redirecting": "Redirecting...",
|
||||||
"common.button.go-back": "Go back",
|
"common.button.go-back": "Go back",
|
||||||
"common.button.go-home": "Go home",
|
"common.button.go-home": "Go home",
|
||||||
"common.notify.copied": "Your link was copied to the clipboard",
|
"common.notify.copied": "Your link was copied to the clipboard",
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export default {
|
|||||||
"signIn.notify.totp-required.title": "Wymagane jest uwierzytelnianie dwuetapowe",
|
"signIn.notify.totp-required.title": "Wymagane jest uwierzytelnianie dwuetapowe",
|
||||||
"signIn.notify.totp-required.description": "Wprowadź kod uwierzytelniania dwuetapowego",
|
"signIn.notify.totp-required.description": "Wprowadź kod uwierzytelniania dwuetapowego",
|
||||||
"signIn.oauth.or": "LUB",
|
"signIn.oauth.or": "LUB",
|
||||||
"signIn.oauth.signInWith": "Sign in with",
|
"signIn.oauth.signInWith": "Zaloguj się przez",
|
||||||
"signIn.oauth.github": "GitHub",
|
"signIn.oauth.github": "GitHub",
|
||||||
"signIn.oauth.google": "Google",
|
"signIn.oauth.google": "Google",
|
||||||
"signIn.oauth.microsoft": "Microsoft",
|
"signIn.oauth.microsoft": "Microsoft",
|
||||||
@@ -152,10 +152,10 @@ export default {
|
|||||||
"account.reverseShares.modal.max-size.label": "Maksymalny rozmiar udziału",
|
"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": "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.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": "Tryb uproszczony",
|
||||||
"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": "Ułatwia wysyłanie pliku do Ciebie. Osoba będzie mogła dostosować nazwę i opis udziałów.",
|
||||||
"account.reverseShares.modal.public-access": "Public access",
|
"account.reverseShares.modal.public-access": "Dostęp publiczny",
|
||||||
"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": "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.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.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.",
|
"account.reverseShare.never-expires": "To udostępnienie odwrotne nigdy nie wygasa.",
|
||||||
@@ -174,7 +174,7 @@ export default {
|
|||||||
// /admin
|
// /admin
|
||||||
"admin.title": "Administracja",
|
"admin.title": "Administracja",
|
||||||
"admin.button.users": "Zarządzanie użytkownikami",
|
"admin.button.users": "Zarządzanie użytkownikami",
|
||||||
"admin.button.shares": "Share management",
|
"admin.button.shares": "Zarządzanie udostępnieniami",
|
||||||
"admin.button.config": "Konfiguracja",
|
"admin.button.config": "Konfiguracja",
|
||||||
"admin.version": "Wersja",
|
"admin.version": "Wersja",
|
||||||
// END /admin
|
// END /admin
|
||||||
@@ -202,13 +202,13 @@ export default {
|
|||||||
"admin.users.modal.create.admin.description": "Jeśli zaznaczone, użytkownik będzie miał dostęp do panelu administratora.",
|
"admin.users.modal.create.admin.description": "Jeśli zaznaczone, użytkownik będzie miał dostęp do panelu administratora.",
|
||||||
// END /admin/users
|
// END /admin/users
|
||||||
// /admin/shares
|
// /admin/shares
|
||||||
"admin.shares.title": "Share management",
|
"admin.shares.title": "Zarządzanie udostępnieniami",
|
||||||
"admin.shares.table.id": "Share ID",
|
"admin.shares.table.id": "ID Udostępnienia",
|
||||||
"admin.shares.table.username": "Creator",
|
"admin.shares.table.username": "Twórca",
|
||||||
"admin.shares.table.visitors": "Visitors",
|
"admin.shares.table.visitors": "Odwiedzający",
|
||||||
"admin.shares.table.expires": "Expires At",
|
"admin.shares.table.expires": "Wygasa",
|
||||||
"admin.shares.edit.delete.title": "Delete share {id}",
|
"admin.shares.edit.delete.title": "Usuń udostępnienie {id}",
|
||||||
"admin.shares.edit.delete.description": "Do you really want to delete this share?",
|
"admin.shares.edit.delete.description": "Czy na pewno chcesz usunąć to udostępnienie?",
|
||||||
// END /admin/shares
|
// END /admin/shares
|
||||||
// /upload
|
// /upload
|
||||||
"upload.title": "Prześlij",
|
"upload.title": "Prześlij",
|
||||||
@@ -244,9 +244,9 @@ export default {
|
|||||||
"upload.modal.expires.month-plural": "Miesiące/ęcy",
|
"upload.modal.expires.month-plural": "Miesiące/ęcy",
|
||||||
"upload.modal.expires.year-singular": "Rok",
|
"upload.modal.expires.year-singular": "Rok",
|
||||||
"upload.modal.expires.year-plural": "Lat/a",
|
"upload.modal.expires.year-plural": "Lat/a",
|
||||||
"upload.modal.accordion.name-and-description.title": "Name and description",
|
"upload.modal.accordion.name-and-description.title": "Nazwa i opis",
|
||||||
"upload.modal.accordion.name-and-description.name.placeholder": "Name",
|
"upload.modal.accordion.name-and-description.name.placeholder": "Nazwa",
|
||||||
"upload.modal.accordion.name-and-description.description.placeholder": "Note for the recipients of this share",
|
"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.title": "Odbiorcy wiadomości e-mail",
|
||||||
"upload.modal.accordion.email.placeholder": "Wprowadź adresatów 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",
|
"upload.modal.accordion.email.invalid-email": "Adres e-mail jest nieprawidłowy",
|
||||||
@@ -259,7 +259,7 @@ export default {
|
|||||||
"upload.modal.completed.never-expires": "To udostępnienie nigdy nie wygaśnie.",
|
"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.expires-on": "To udostępnienie wygaśnie dnia {expiration}.",
|
||||||
"upload.modal.completed.share-ready": "Udostępnianie gotowe",
|
"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
|
// END /upload
|
||||||
// /share/[id]
|
// /share/[id]
|
||||||
"share.title": "Udostępnij {shareId}",
|
"share.title": "Udostępnij {shareId}",
|
||||||
@@ -269,8 +269,8 @@ export default {
|
|||||||
"share.error.removed.title": "Udostępnianie usunięte",
|
"share.error.removed.title": "Udostępnianie usunięte",
|
||||||
"share.error.not-found.title": "Nie znaleziono udziału",
|
"share.error.not-found.title": "Nie znaleziono udziału",
|
||||||
"share.error.not-found.description": "Udział, który szukasz, nie istnieje.",
|
"share.error.not-found.description": "Udział, który szukasz, nie istnieje.",
|
||||||
"share.error.access-denied.title": "Private share",
|
"share.error.access-denied.title": "Prywatne udostępnienie",
|
||||||
"share.error.access-denied.description": "The current account does not have permission to access this share",
|
"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.title": "Wymagane hasło",
|
||||||
"share.modal.password.description": "Aby uzyskać dostęp do tego udziału, wprowadź hasło.",
|
"share.modal.password.description": "Aby uzyskać dostęp do tego udziału, wprowadź hasło.",
|
||||||
"share.modal.password": "Hasło",
|
"share.modal.password": "Hasło",
|
||||||
@@ -302,8 +302,8 @@ export default {
|
|||||||
"admin.config.general.app-url.description": "Pod którym adresem URL Pingvin Share jest dostępny",
|
"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": "Pokaż stronę główną",
|
||||||
"admin.config.general.show-home-page.description": "Czy wyświetlać 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": "Czas trwania sesji",
|
||||||
"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.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": "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.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",
|
"admin.config.general.logo.placeholder": "Wybierz obraz",
|
||||||
@@ -324,7 +324,7 @@ export default {
|
|||||||
"admin.config.email.invite-subject": "Temat zaproszenia",
|
"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-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": "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": "Zezwól na rejestrację",
|
||||||
"admin.config.share.allow-registration.description": "Czy dozwolona jest rejestracja",
|
"admin.config.share.allow-registration.description": "Czy dozwolona jest rejestracja",
|
||||||
"admin.config.share.allow-unauthenticated-shares": "Zezwalaj na nieuwierzytelnione udostępnianie",
|
"admin.config.share.allow-unauthenticated-shares": "Zezwalaj na nieuwierzytelnione udostępnianie",
|
||||||
@@ -335,8 +335,8 @@ export default {
|
|||||||
"admin.config.share.max-size.description": "Maksymalny rozmiar udziału w bajtach",
|
"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": "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.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": "Rozmiar fragmentu",
|
||||||
"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": "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": "Auto open create share modal",
|
||||||
"admin.config.share.auto-open-share-modal.description": "The share creation modal automatically appears when a user selects files, eliminating the need to manually click the button.",
|
"admin.config.share.auto-open-share-modal.description": "The share creation modal automatically appears when a user selects files, eliminating the need to manually click the button.",
|
||||||
"admin.config.smtp.enabled": "Włączony",
|
"admin.config.smtp.enabled": "Włączony",
|
||||||
|
|||||||
@@ -324,7 +324,7 @@ export default {
|
|||||||
"admin.config.email.invite-subject": "Assunto do convite",
|
"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-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": "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": "Permitir novos registos",
|
||||||
"admin.config.share.allow-registration.description": "Se o registro é permitido",
|
"admin.config.share.allow-registration.description": "Se o registro é permitido",
|
||||||
"admin.config.share.allow-unauthenticated-shares": "Permitir compartilhamentos sem autenticação",
|
"admin.config.share.allow-unauthenticated-shares": "Permitir compartilhamentos sem autenticação",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import jwtDecode from "jwt-decode";
|
import { jwtDecode } from "jwt-decode";
|
||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import configService from "./services/config.service";
|
import configService from "./services/config.service";
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
import { useForm, yupResolver } from "@mantine/form";
|
import { useForm, yupResolver } from "@mantine/form";
|
||||||
import { useModals } from "@mantine/modals";
|
import { useModals } from "@mantine/modals";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Tb2Fa } from "react-icons/tb";
|
import { TbAuth2Fa } from "react-icons/tb";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
import Meta from "../../components/Meta";
|
import Meta from "../../components/Meta";
|
||||||
@@ -293,7 +293,7 @@ const Account = () => {
|
|||||||
|
|
||||||
<Tabs defaultValue="totp">
|
<Tabs defaultValue="totp">
|
||||||
<Tabs.List>
|
<Tabs.List>
|
||||||
<Tabs.Tab value="totp" icon={<Tb2Fa size={14} />}>
|
<Tabs.Tab value="totp" icon={<TbAuth2Fa size={14} />}>
|
||||||
TOTP
|
TOTP
|
||||||
</Tabs.Tab>
|
</Tabs.Tab>
|
||||||
</Tabs.List>
|
</Tabs.List>
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "pingvin-share",
|
"name": "pingvin-share",
|
||||||
"version": "1.0.3",
|
"version": "1.1.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "pingvin-share",
|
"name": "pingvin-share",
|
||||||
"version": "1.0.3",
|
"version": "1.1.1",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"conventional-changelog-cli": "^3.0.0"
|
"conventional-changelog-cli": "^3.0.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "pingvin-share",
|
"name": "pingvin-share",
|
||||||
"version": "1.0.3",
|
"version": "1.1.1",
|
||||||
"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",
|
||||||
"version": "conventional-changelog -p conventionalcommits -i CHANGELOG.md -s && git add CHANGELOG.md",
|
"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: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 ."
|
"deploy:dev": "docker buildx build --push --tag stonith404/pingvin-share:development --platform linux/amd64,linux/arm64 ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
7
reverse-proxy/Caddyfile
Normal file
7
reverse-proxy/Caddyfile
Normal 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
|
||||||
|
}
|
||||||
14
reverse-proxy/Caddyfile.trust-proxy
Normal file
14
reverse-proxy/Caddyfile.trust-proxy
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,10 +4,17 @@
|
|||||||
cp -rn /tmp/img/* /opt/app/frontend/public/img
|
cp -rn /tmp/img/* /opt/app/frontend/public/img
|
||||||
|
|
||||||
# Start Caddy
|
# 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
|
# Run the frontend server
|
||||||
PORT=3333 HOSTNAME=0.0.0.0 node frontend/server.js &
|
PORT=3333 HOSTNAME=0.0.0.0 node frontend/server.js &
|
||||||
|
|
||||||
# Run the backend server
|
# Run the backend server
|
||||||
cd backend && npm run prod
|
cd backend && npm run prod
|
||||||
|
|
||||||
# Wait for all processes to finish
|
# Wait for all processes to finish
|
||||||
wait -n
|
wait -n
|
||||||
|
|||||||
Reference in New Issue
Block a user