Compare commits

..

16 Commits

Author SHA1 Message Date
Elias Schneider
905bab9c86 release: 0.3.4 2022-12-10 15:46:53 +01:00
Elias Schneider
7e877ce9f4 fix: show alternative to copy button if site is not using https 2022-12-10 13:16:23 +01:00
Elias Schneider
b1bfb09dfd fix: tables on mobile 2022-12-09 14:37:09 +01:00
Elias Schneider
c8a4521677 fix: sign up page available when registration is disabled 2022-12-09 12:05:43 +01:00
Elias Schneider
3c74cc14df release: 0.3.3 2022-12-08 23:22:59 +01:00
Elias Schneider
a165f8ec4d refactor: remove console log 2022-12-08 23:22:15 +01:00
Elias Schneider
d6a88f2a22 performance: reduce docker image size 2022-12-08 23:21:31 +01:00
Elias Schneider
b8172efd59 fix: allow empty strings in config variable 2022-12-08 23:21:16 +01:00
Elias Schneider
cbe37c6798 fix: obscured text length 2022-12-08 23:12:25 +01:00
Elias Schneider
a545c44426 fix: improve admin dashboard color and layout 2022-12-08 22:43:14 +01:00
Elias Schneider
08a2f60f72 chore: add migration for v0.3.3 2022-12-08 21:58:58 +01:00
Elias Schneider
907e56af0f fix: space character in email 2022-12-08 20:04:56 +01:00
Elias Schneider
888a0c5faf feat: add support for different email and user 2022-12-08 20:00:04 +01:00
Elias Schneider
bfb0d151ea fix: obscure critical config variables 2022-12-08 19:14:06 +01:00
Elias Schneider
1f63f22591 docs: add review to README 2022-12-08 17:30:12 +01:00
Elias Schneider
a2d5e0f72c test: fix system tests not await backend start 2022-12-07 13:44:02 +01:00
24 changed files with 407 additions and 125 deletions

View File

@@ -1,3 +1,28 @@
### [0.3.4](https://github.com/stonith404/pingvin-share/compare/v0.3.3...v0.3.4) (2022-12-10)
### Bug Fixes
* show alternative to copy button if site is not using https ([7e877ce](https://github.com/stonith404/pingvin-share/commit/7e877ce9f4b82d61c9b238e17def9f4c29e7aeb8))
* sign up page available when registration is disabled ([c8a4521](https://github.com/stonith404/pingvin-share/commit/c8a4521677280d6aba89d293a1fe0c38adf9f92c))
* tables on mobile ([b1bfb09](https://github.com/stonith404/pingvin-share/commit/b1bfb09dfd5c90cc18847470a9ce1ce8397c1476))
### [0.3.3](https://github.com/stonith404/pingvin-share/compare/v0.3.2...v0.3.3) (2022-12-08)
### Features
* add support for different email and user ([888a0c5](https://github.com/stonith404/pingvin-share/commit/888a0c5fafc51b6872ed71e37d4b40c9bf6a07f1))
### Bug Fixes
* allow empty strings in config variable ([b8172ef](https://github.com/stonith404/pingvin-share/commit/b8172efd59fb3271ab9b818b13a7003342b2cebd))
* improve admin dashboard color and layout ([a545c44](https://github.com/stonith404/pingvin-share/commit/a545c444261c90105dcb165ebcf4b26634e729ca))
* obscure critical config variables ([bfb0d15](https://github.com/stonith404/pingvin-share/commit/bfb0d151ea2ba125e536a16b1873e143a67e9f64))
* obscured text length ([cbe37c6](https://github.com/stonith404/pingvin-share/commit/cbe37c679853ecef1522ed213e4cac5defd5b45a))
* space character in email ([907e56a](https://github.com/stonith404/pingvin-share/commit/907e56af0faccdbc8d7f5ab3418a4ad71ff849f5))
### [0.3.2](https://github.com/stonith404/pingvin-share/compare/v0.3.1...v0.3.2) (2022-12-07) ### [0.3.2](https://github.com/stonith404/pingvin-share/compare/v0.3.1...v0.3.2) (2022-12-07)

View File

@@ -1,11 +1,12 @@
FROM node:18-alpine AS frontend-builder FROM node:18-slim AS frontend-builder
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
COPY ./frontend . COPY ./frontend .
RUN npm run build RUN npm run build
FROM node:18 AS backend-builder FROM node:18-slim AS backend-builder
RUN apt-get update && apt-get install -y openssl
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
@@ -13,9 +14,10 @@ COPY ./backend .
RUN npx prisma generate RUN npx prisma generate
RUN npm run build RUN npm run build
FROM node:18 AS runner FROM node:18-slim AS runner
WORKDIR /opt/app/frontend
ENV NODE_ENV=production ENV NODE_ENV=production
RUN apt-get update && apt-get install -y openssl
WORKDIR /opt/app/frontend
COPY --from=frontend-builder /opt/app/next.config.js . COPY --from=frontend-builder /opt/app/next.config.js .
COPY --from=frontend-builder /opt/app/public ./public COPY --from=frontend-builder /opt/app/public ./public
COPY --from=frontend-builder /opt/app/.next ./.next COPY --from=frontend-builder /opt/app/.next ./.next
@@ -26,7 +28,7 @@ COPY --from=backend-builder /opt/app/node_modules ./node_modules
COPY --from=backend-builder /opt/app/dist ./dist 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 ./
WORKDIR /opt/app
WORKDIR /opt/app
EXPOSE 3000 EXPOSE 3000
CMD cd frontend && node_modules/.bin/next start & cd backend && npm run prod CMD cd frontend && node_modules/.bin/next start & cd backend && npm run prod

View File

@@ -2,12 +2,6 @@
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.
## 🎪 Showcase
Demo: https://pingvin-share.dev.eliasschneider.com
<img src="https://user-images.githubusercontent.com/58886915/167101708-b85032ad-f5b1-480a-b8d7-ec0096ea2a43.png" width="700"/>
## ✨ Features ## ✨ Features
- Spin up your instance within 2 minutes - Spin up your instance within 2 minutes
@@ -18,6 +12,13 @@ Demo: https://pingvin-share.dev.eliasschneider.com
- Email recepients - Email recepients
- Light & dark mode - Light & dark mode
## 🐧 Get to know Pingvin Share
- [Demo](https://pingvin-share.dev.eliasschneider.com)
- [Review by DB Tech](https://www.youtube.com/watch?v=rWwNeZCOPJA)
<img src="https://user-images.githubusercontent.com/58886915/167101708-b85032ad-f5b1-480a-b8d7-ec0096ea2a43.png" width="700"/>
## ⌨️ Setup ## ⌨️ Setup
> Pleas note that Pingvin Share is in early stage and could include some bugs > Pleas note that Pingvin Share is in early stage and could include some bugs

View File

@@ -60,7 +60,8 @@
"ts-loader": "^9.4.2", "ts-loader": "^9.4.2",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"tsconfig-paths": "4.1.1", "tsconfig-paths": "4.1.1",
"typescript": "^4.9.3" "typescript": "^4.9.3",
"wait-on": "^6.0.1"
} }
}, },
"node_modules/@angular-devkit/core": { "node_modules/@angular-devkit/core": {
@@ -390,6 +391,21 @@
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true "dev": true
}, },
"node_modules/@hapi/hoek": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
"integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==",
"dev": true
},
"node_modules/@hapi/topo": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz",
"integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==",
"dev": true,
"dependencies": {
"@hapi/hoek": "^9.0.0"
}
},
"node_modules/@humanwhocodes/config-array": { "node_modules/@humanwhocodes/config-array": {
"version": "0.11.7", "version": "0.11.7",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz",
@@ -1027,6 +1043,27 @@
"integrity": "sha512-Bd4LZ+WAnUHOq31e9X/ihi5zPlr4SzTRwUZZYxvWOxlerIZ7HJlVa9zXpuKTKLpI9O1l8Ec4OYCKsivWCs5a3Q==", "integrity": "sha512-Bd4LZ+WAnUHOq31e9X/ihi5zPlr4SzTRwUZZYxvWOxlerIZ7HJlVa9zXpuKTKLpI9O1l8Ec4OYCKsivWCs5a3Q==",
"dev": true "dev": true
}, },
"node_modules/@sideway/address": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz",
"integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==",
"dev": true,
"dependencies": {
"@hapi/hoek": "^9.0.0"
}
},
"node_modules/@sideway/formula": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz",
"integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==",
"dev": true
},
"node_modules/@sideway/pinpoint": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz",
"integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==",
"dev": true
},
"node_modules/@tsconfig/node10": { "node_modules/@tsconfig/node10": {
"version": "1.0.9", "version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
@@ -1983,6 +2020,15 @@
"integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
"dev": true "dev": true
}, },
"node_modules/axios": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz",
"integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==",
"dev": true,
"dependencies": {
"follow-redirects": "^1.14.7"
}
},
"node_modules/balanced-match": { "node_modules/balanced-match": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -3518,6 +3564,26 @@
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
"dev": true "dev": true
}, },
"node_modules/follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/forever-agent": { "node_modules/forever-agent": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
@@ -4264,6 +4330,19 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/joi": {
"version": "17.7.0",
"resolved": "https://registry.npmjs.org/joi/-/joi-17.7.0.tgz",
"integrity": "sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg==",
"dev": true,
"dependencies": {
"@hapi/hoek": "^9.0.0",
"@hapi/topo": "^5.0.0",
"@sideway/address": "^4.1.3",
"@sideway/formula": "^3.0.0",
"@sideway/pinpoint": "^2.0.0"
}
},
"node_modules/js-sdsl": { "node_modules/js-sdsl": {
"version": "4.1.5", "version": "4.1.5",
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz",
@@ -7134,6 +7213,25 @@
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
"dev": true "dev": true
}, },
"node_modules/wait-on": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/wait-on/-/wait-on-6.0.1.tgz",
"integrity": "sha512-zht+KASY3usTY5u2LgaNqn/Cd8MukxLGjdcZxT2ns5QzDmTFc4XoWBgC+C/na+sMRZTuVygQoMYwdcVjHnYIVw==",
"dev": true,
"dependencies": {
"axios": "^0.25.0",
"joi": "^17.6.0",
"lodash": "^4.17.21",
"minimist": "^1.2.5",
"rxjs": "^7.5.4"
},
"bin": {
"wait-on": "bin/wait-on"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/watchpack": { "node_modules/watchpack": {
"version": "2.4.0", "version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
@@ -7704,6 +7802,21 @@
} }
} }
}, },
"@hapi/hoek": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
"integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==",
"dev": true
},
"@hapi/topo": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz",
"integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==",
"dev": true,
"requires": {
"@hapi/hoek": "^9.0.0"
}
},
"@humanwhocodes/config-array": { "@humanwhocodes/config-array": {
"version": "0.11.7", "version": "0.11.7",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz",
@@ -8138,6 +8251,27 @@
"integrity": "sha512-Bd4LZ+WAnUHOq31e9X/ihi5zPlr4SzTRwUZZYxvWOxlerIZ7HJlVa9zXpuKTKLpI9O1l8Ec4OYCKsivWCs5a3Q==", "integrity": "sha512-Bd4LZ+WAnUHOq31e9X/ihi5zPlr4SzTRwUZZYxvWOxlerIZ7HJlVa9zXpuKTKLpI9O1l8Ec4OYCKsivWCs5a3Q==",
"dev": true "dev": true
}, },
"@sideway/address": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz",
"integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==",
"dev": true,
"requires": {
"@hapi/hoek": "^9.0.0"
}
},
"@sideway/formula": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz",
"integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==",
"dev": true
},
"@sideway/pinpoint": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz",
"integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==",
"dev": true
},
"@tsconfig/node10": { "@tsconfig/node10": {
"version": "1.0.9", "version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
@@ -8925,6 +9059,15 @@
"integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
"dev": true "dev": true
}, },
"axios": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz",
"integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==",
"dev": true,
"requires": {
"follow-redirects": "^1.14.7"
}
},
"balanced-match": { "balanced-match": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -10090,6 +10233,12 @@
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
"dev": true "dev": true
}, },
"follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
"dev": true
},
"forever-agent": { "forever-agent": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
@@ -10637,6 +10786,19 @@
"resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz",
"integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==" "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q=="
}, },
"joi": {
"version": "17.7.0",
"resolved": "https://registry.npmjs.org/joi/-/joi-17.7.0.tgz",
"integrity": "sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg==",
"dev": true,
"requires": {
"@hapi/hoek": "^9.0.0",
"@hapi/topo": "^5.0.0",
"@sideway/address": "^4.1.3",
"@sideway/formula": "^3.0.0",
"@sideway/pinpoint": "^2.0.0"
}
},
"js-sdsl": { "js-sdsl": {
"version": "4.1.5", "version": "4.1.5",
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz",
@@ -12786,6 +12948,19 @@
} }
} }
}, },
"wait-on": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/wait-on/-/wait-on-6.0.1.tgz",
"integrity": "sha512-zht+KASY3usTY5u2LgaNqn/Cd8MukxLGjdcZxT2ns5QzDmTFc4XoWBgC+C/na+sMRZTuVygQoMYwdcVjHnYIVw==",
"dev": true,
"requires": {
"axios": "^0.25.0",
"joi": "^17.6.0",
"lodash": "^4.17.21",
"minimist": "^1.2.5",
"rxjs": "^7.5.4"
}
},
"watchpack": { "watchpack": {
"version": "2.4.0", "version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",

View File

@@ -7,7 +7,7 @@
"prod": "prisma migrate deploy && prisma db seed && node dist/src/main", "prod": "prisma migrate deploy && prisma db seed && node dist/src/main",
"lint": "eslint 'src/**/*.ts'", "lint": "eslint 'src/**/*.ts'",
"format": "prettier --write 'src/**/*.ts'", "format": "prettier --write 'src/**/*.ts'",
"test:system": "prisma migrate reset -f && nest start & sleep 10 && newman run ./test/system/newman-system-tests.json" "test:system": "prisma migrate reset -f && nest start & wait-on http://localhost:8080/api/configs && newman run ./test/system/newman-system-tests.json"
}, },
"prisma": { "prisma": {
"seed": "ts-node prisma/seed/config.seed.ts" "seed": "ts-node prisma/seed/config.seed.ts"
@@ -65,6 +65,7 @@
"ts-loader": "^9.4.2", "ts-loader": "^9.4.2",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"tsconfig-paths": "4.1.1", "tsconfig-paths": "4.1.1",
"typescript": "^4.9.3" "typescript": "^4.9.3",
"wait-on": "^6.0.1"
} }
} }

View File

@@ -0,0 +1,17 @@
-- RedefineTables
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_Config" (
"updatedAt" DATETIME NOT NULL,
"key" TEXT NOT NULL PRIMARY KEY,
"type" TEXT NOT NULL,
"value" TEXT NOT NULL,
"description" TEXT NOT NULL,
"obscured" BOOLEAN NOT NULL DEFAULT false,
"secret" BOOLEAN NOT NULL DEFAULT true,
"locked" BOOLEAN NOT NULL DEFAULT false
);
INSERT INTO "new_Config" ("description", "key", "locked", "secret", "type", "updatedAt", "value") SELECT "description", "key", "locked", "secret", "type", "updatedAt", "value" FROM "Config";
DROP TABLE "Config";
ALTER TABLE "new_Config" RENAME TO "Config";
PRAGMA foreign_key_check;
PRAGMA foreign_keys=ON;

View File

@@ -84,6 +84,7 @@ model Config {
type String type String
value String value String
description String description String
obscured Boolean @default(false)
secret Boolean @default(true) secret Boolean @default(true)
locked Boolean @default(false) locked Boolean @default(false)
} }

View File

@@ -1,7 +1,7 @@
import { PrismaClient } from "@prisma/client"; import { Prisma, PrismaClient } from "@prisma/client";
import * as crypto from "crypto"; import * as crypto from "crypto";
const configVariables = [ const configVariables: Prisma.ConfigCreateInput[] = [
{ {
key: "SETUP_FINISHED", key: "SETUP_FINISHED",
description: "Whether the setup has been finished", description: "Whether the setup has been finished",
@@ -55,7 +55,7 @@ const configVariables = [
{ {
key: "ENABLE_EMAIL_RECIPIENTS", key: "ENABLE_EMAIL_RECIPIENTS",
description: description:
"Whether to send emails to recipients. Only set this to true if you entered the host, port, email and password of your SMTP server.", "Whether to send emails to recipients. Only set this to true if you entered the host, port, email, user and password of your SMTP server.",
type: "boolean", type: "boolean",
value: "false", value: "false",
secret: false, secret: false,
@@ -74,7 +74,13 @@ const configVariables = [
}, },
{ {
key: "SMTP_EMAIL", key: "SMTP_EMAIL",
description: "Email address of the SMTP server", description: "Email address which the emails get sent from",
type: "string",
value: "",
},
{
key: "SMTP_USERNAME",
description: "Username of the SMTP server",
type: "string", type: "string",
value: "", value: "",
}, },
@@ -83,6 +89,7 @@ const configVariables = [
description: "Password of the SMTP server", description: "Password of the SMTP server",
type: "string", type: "string",
value: "", value: "",
obscured: true,
}, },
]; ];
@@ -102,14 +109,34 @@ async function main() {
} }
} }
// Delete the config variable if it doesn't exist anymore
const configVariablesFromDatabase = await prisma.config.findMany(); const configVariablesFromDatabase = await prisma.config.findMany();
// Delete the config variable if it doesn't exist anymore
for (const configVariableFromDatabase of configVariablesFromDatabase) { for (const configVariableFromDatabase of configVariablesFromDatabase) {
if (!configVariables.find((v) => v.key == configVariableFromDatabase.key)) { const configVariable = configVariables.find(
(v) => v.key == configVariableFromDatabase.key
);
if (!configVariable) {
await prisma.config.delete({ await prisma.config.delete({
where: { key: configVariableFromDatabase.key }, where: { key: configVariableFromDatabase.key },
}); });
// Update the config variable if the metadata changed
} else if (
JSON.stringify({
...configVariable,
key: configVariableFromDatabase.key,
value: configVariableFromDatabase.value,
}) != JSON.stringify(configVariableFromDatabase)
) {
await prisma.config.update({
where: { key: configVariableFromDatabase.key },
data: {
...configVariable,
key: configVariableFromDatabase.key,
value: configVariableFromDatabase.value,
},
});
} }
} }
} }

View File

@@ -11,6 +11,9 @@ export class AdminConfigDTO extends ConfigDTO {
@Expose() @Expose()
description: string; description: string;
@Expose()
obscured: boolean;
from(partial: Partial<AdminConfigDTO>) { from(partial: Partial<AdminConfigDTO>) {
return plainToClass(AdminConfigDTO, partial, { return plainToClass(AdminConfigDTO, partial, {
excludeExtraneousValues: true, excludeExtraneousValues: true,

View File

@@ -1,7 +1,8 @@
import { IsNotEmpty } from "class-validator"; import { IsNotEmpty, ValidateIf } from "class-validator";
class UpdateConfigDTO { class UpdateConfigDTO {
@IsNotEmpty() @IsNotEmpty()
@ValidateIf((dto) => dto.value !== "")
value: string | number | boolean; value: string | number | boolean;
} }

View File

@@ -14,7 +14,7 @@ export class EmailService {
port: parseInt(this.config.get("SMTP_PORT")), port: parseInt(this.config.get("SMTP_PORT")),
secure: parseInt(this.config.get("SMTP_PORT")) == 465, secure: parseInt(this.config.get("SMTP_PORT")) == 465,
auth: { auth: {
user: this.config.get("SMTP_EMAIL"), user: this.config.get("SMTP_USERNAME"),
pass: this.config.get("SMTP_PASSWORD"), pass: this.config.get("SMTP_PASSWORD"),
}, },
}); });
@@ -28,7 +28,7 @@ export class EmailService {
from: `"Pingvin Share" <${this.config.get("SMTP_EMAIL")}>`, from: `"Pingvin Share" <${this.config.get("SMTP_EMAIL")}>`,
to: recipientEmail, to: recipientEmail,
subject: "Files shared with you", subject: "Files shared with you",
text: `Hey!\n${creator.username} shared some files with you. View or dowload the files with this link: ${shareUrl}.\n Shared securely with Pingvin Share 🐧`, text: `Hey!\n${creator.username} shared some files with you. View or dowload the files with this link: ${shareUrl}.\nShared securely with Pingvin Share 🐧`,
}); });
} }
} }

View File

@@ -0,0 +1,16 @@
import { Stack, TextInput } from "@mantine/core";
import { ModalsContextProps } from "@mantine/modals/lib/context";
const showShareLinkModal = (modals: ModalsContextProps, shareId: string) => {
const link = `${window.location.origin}/share/${shareId}`;
return modals.openModal({
title: "Share link",
children: (
<Stack align="stretch">
<TextInput variant="filled" value={link} />
</Stack>
),
});
};
export default showShareLinkModal;

View File

@@ -1,4 +1,12 @@
import { ActionIcon, Code, Group, Skeleton, Table, Text } from "@mantine/core"; import {
ActionIcon,
Box,
Code,
Group,
Skeleton,
Table,
Text,
} from "@mantine/core";
import { useModals } from "@mantine/modals"; import { useModals } from "@mantine/modals";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { TbEdit, TbLock } from "react-icons/tb"; import { TbEdit, TbLock } from "react-icons/tb";
@@ -43,50 +51,55 @@ const AdminConfigTable = () => {
)); ));
return ( return (
<Table verticalSpacing="sm" horizontalSpacing="xl" withBorder> <Box sx={{ display: "block", overflowX: "auto" }}>
<thead> <Table verticalSpacing="sm" horizontalSpacing="xl" withBorder>
<tr> <thead>
<th>Key</th> <tr>
<th>Value</th> <th>Key</th>
<th></th> <th>Value</th>
</tr> <th></th>
</thead> </tr>
<tbody> </thead>
{isLoading <tbody>
? skeletonRows {isLoading
: configVariables.map((element) => ( ? skeletonRows
<tr key={element.key}> : configVariables.map((configVariable) => (
<td style={{ maxWidth: "200px" }}> <tr key={configVariable.key}>
<Code>{element.key}</Code> {element.secret && <TbLock />}{" "} <td style={{ maxWidth: "200px" }}>
<br /> <Code>{configVariable.key}</Code>{" "}
<Text size="xs" color="dimmed"> {configVariable.secret && <TbLock />} <br />
{element.description} <Text size="xs" color="dimmed">
</Text> {configVariable.description}
</td> </Text>
<td>{element.value}</td> </td>
<td>
<td> {configVariable.obscured
<Group position="right"> ? "•".repeat(configVariable.value.length)
<ActionIcon : configVariable.value}
color="primary" </td>
variant="light" <td>
size={25} <Group position="right">
onClick={() => <ActionIcon
showUpdateConfigVariableModal( color="primary"
modals, variant="light"
element, size={25}
getConfigVariables onClick={() =>
) showUpdateConfigVariableModal(
} modals,
> configVariable,
<TbEdit /> getConfigVariables
</ActionIcon> )
</Group> }
</td> >
</tr> <TbEdit />
))} </ActionIcon>
</tbody> </Group>
</Table> </td>
</tr>
))}
</tbody>
</Table>
</Box>
); );
}; };

View File

@@ -18,8 +18,8 @@ const ManageUserTable = ({
const modals = useModals(); const modals = useModals();
return ( return (
<Box sx={{ display: "block", overflowX: "auto", whiteSpace: "nowrap" }}> <Box sx={{ display: "block", overflowX: "auto" }}>
<Table verticalSpacing="sm" highlightOnHover> <Table verticalSpacing="sm">
<thead> <thead>
<tr> <tr>
<th>Username</th> <th>Username</th>

View File

@@ -50,7 +50,6 @@ const Body = ({
<Stack> <Stack>
<form <form
onSubmit={form.onSubmit(async (values) => { onSubmit={form.onSubmit(async (values) => {
console.log(values);
userService userService
.create(values) .create(values)
.then(() => { .then(() => {
@@ -62,10 +61,7 @@ const Body = ({
> >
<Stack> <Stack>
<TextInput label="Username" {...form.getInputProps("username")} /> <TextInput label="Username" {...form.getInputProps("username")} />
<TextInput <TextInput label="Email" {...form.getInputProps("email")} />
label="Email"
{...form.getInputProps("email")}
/>
<PasswordInput <PasswordInput
label="New password" label="New password"
{...form.getInputProps("password")} {...form.getInputProps("password")}

View File

@@ -2,6 +2,7 @@ import {
Button, Button,
Code, Code,
NumberInput, NumberInput,
PasswordInput,
Select, Select,
Space, Space,
Stack, Stack,
@@ -53,9 +54,12 @@ const Body = ({
<Text> <Text>
Set <Code>{configVariable.key}</Code> to Set <Code>{configVariable.key}</Code> to
</Text> </Text>
{configVariable.type == "string" && ( {configVariable.type == "string" &&
<TextInput label="Value" {...form.getInputProps("stringValue")} /> (configVariable.obscured ? (
)} <PasswordInput label="Value" {...form.getInputProps("stringValue")} />
) : (
<TextInput label="Value" {...form.getInputProps("stringValue")} />
))}
{configVariable.type == "number" && ( {configVariable.type == "number" && (
<NumberInput label="Value" {...form.getInputProps("numberValue")} /> <NumberInput label="Value" {...form.getInputProps("numberValue")} />
)} )}

View File

@@ -79,10 +79,7 @@ const Body = ({
label="Username" label="Username"
{...accountForm.getInputProps("username")} {...accountForm.getInputProps("username")}
/> />
<TextInput <TextInput label="Email" {...accountForm.getInputProps("email")} />
label="Email"
{...accountForm.getInputProps("email")}
/>
<Switch <Switch
mt="xs" mt="xs"
labelPosition="left" labelPosition="left"

View File

@@ -40,14 +40,16 @@ const Body = ({ share }: { share: Share }) => {
variant="filled" variant="filled"
value={link} value={link}
rightSection={ rightSection={
<ActionIcon window.isSecureContext && (
onClick={() => { <ActionIcon
clipboard.copy(link); onClick={() => {
toast.success("Your link was copied to the keyboard."); clipboard.copy(link);
}} toast.success("Your link was copied to the keyboard.");
> }}
<TbCopy /> >
</ActionIcon> <TbCopy />
</ActionIcon>
)
} }
/> />
<Text <Text

View File

@@ -80,10 +80,7 @@ const Account = () => {
label="Username" label="Username"
{...accountForm.getInputProps("username")} {...accountForm.getInputProps("username")}
/> />
<TextInput <TextInput label="Email" {...accountForm.getInputProps("email")} />
label="Email"
{...accountForm.getInputProps("email")}
/>
<Group position="right"> <Group position="right">
<Button type="submit">Save</Button> <Button type="submit">Save</Button>
</Group> </Group>

View File

@@ -17,6 +17,7 @@ import Link from "next/link";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { TbLink, TbTrash } from "react-icons/tb"; import { TbLink, TbTrash } from "react-icons/tb";
import showShareLinkModal from "../../components/account/showShareLinkModal";
import Meta from "../../components/Meta"; import Meta from "../../components/Meta";
import useUser from "../../hooks/user.hook"; import useUser from "../../hooks/user.hook";
import shareService from "../../services/share.service"; import shareService from "../../services/share.service";
@@ -83,12 +84,16 @@ const MyShares = () => {
variant="light" variant="light"
size={25} size={25}
onClick={() => { onClick={() => {
clipboard.copy( if (window.isSecureContext) {
`${window.location.origin}/share/${share.id}` clipboard.copy(
); `${window.location.origin}/share/${share.id}`
toast.success( );
"Your link was copied to the keyboard." toast.success(
); "Your link was copied to the keyboard."
);
} else {
showShareLinkModal(modals, share.id);
}
}} }}
> >
<TbLink /> <TbLink />

View File

@@ -1,4 +1,4 @@
import { Col, Container, createStyles, Grid, Paper, Text } from "@mantine/core"; import { Col, createStyles, Grid, Paper, Text } from "@mantine/core";
import Link from "next/link"; import Link from "next/link";
import { TbSettings, TbUsers } from "react-icons/tb"; import { TbSettings, TbUsers } from "react-icons/tb";
@@ -34,28 +34,26 @@ const Admin = () => {
const { classes, theme } = useStyles(); const { classes, theme } = useStyles();
return ( return (
<Container size="xl"> <Paper withBorder p={40}>
<Paper withBorder radius="md" p={40}> <Grid mt="md">
<Grid mt="md"> {managementOptions.map((item) => {
{managementOptions.map((item) => { return (
return ( <Col xs={6} key={item.route}>
<Col xs={6} key={item.route}> <Paper
<Paper withBorder
withBorder component={Link}
component={Link} href={item.route}
href={item.route} key={item.title}
key={item.title} className={classes.item}
className={classes.item} >
> <item.icon color={theme.colors.victoria[8]} size={35} />
<item.icon color={theme.colors.victoria[5]} size={35} /> <Text mt={7}>{item.title}</Text>
<Text mt={7}>{item.title}</Text> </Paper>
</Paper> </Col>
</Col> );
); })}
})} </Grid>
</Grid> </Paper>
</Paper>
</Container>
); );
}; };

View File

@@ -10,7 +10,7 @@ const SignUp = () => {
const router = useRouter(); const router = useRouter();
if (user) { if (user) {
router.replace("/"); router.replace("/");
} else if (config.get("ALLOW_REGISTRATION") == "false") { } else if (!config.get("ALLOW_REGISTRATION")) {
router.replace("/auth/signIn"); router.replace("/auth/signIn");
} else { } else {
return ( return (

View File

@@ -8,6 +8,7 @@ export type AdminConfig = Config & {
updatedAt: Date; updatedAt: Date;
secret: boolean; secret: boolean;
description: string; description: string;
obscured: boolean;
}; };
export default Config; export default Config;

View File

@@ -1,6 +1,6 @@
{ {
"name": "pingvin-share", "name": "pingvin-share",
"version": "0.3.2", "version": "0.3.4",
"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",