Compare commits

..

16 Commits

Author SHA1 Message Date
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
Elias Schneider
c0d0f6fa90 release: 0.3.2 2022-12-07 12:41:14 +01:00
Elias Schneider
4a016ed57d fix: unauthenticated dialog not shown 2022-12-06 11:05:04 +01:00
Elias Schneider
5ea63fb60b fix: use session storage for share token 2022-12-06 10:54:17 +01:00
Elias Schneider
57cb683c64 fix: make share password optional 2022-12-05 23:58:18 +01:00
23 changed files with 337 additions and 91 deletions

View File

@@ -1,3 +1,28 @@
### [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)
### Bug Fixes
* make share password optional ([57cb683](https://github.com/stonith404/pingvin-share/commit/57cb683c64eaedec2697ea6863948bd2ae68dd75))
* unauthenticated dialog not shown ([4a016ed](https://github.com/stonith404/pingvin-share/commit/4a016ed57db526ee900c567f7b7f0991f948c631))
* use session storage for share token ([5ea63fb](https://github.com/stonith404/pingvin-share/commit/5ea63fb60be0c508c38ba228cc8ac6dd7b403aac))
### [0.3.1](https://github.com/stonith404/pingvin-share/compare/v0.3.0...v0.3.1) (2022-12-05) ### [0.3.1](https://github.com/stonith404/pingvin-share/compare/v0.3.0...v0.3.1) (2022-12-05)

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

@@ -1,6 +1,7 @@
import { IsString } from "class-validator"; import { IsOptional, IsString } from "class-validator";
export class SharePasswordDto { export class SharePasswordDto {
@IsString() @IsString()
@IsOptional()
password: string; password: string;
} }

View File

@@ -54,17 +54,20 @@ const AdminConfigTable = () => {
<tbody> <tbody>
{isLoading {isLoading
? skeletonRows ? skeletonRows
: configVariables.map((element) => ( : configVariables.map((configVariable) => (
<tr key={element.key}> <tr key={configVariable.key}>
<td style={{ maxWidth: "200px" }}> <td style={{ maxWidth: "200px" }}>
<Code>{element.key}</Code> {element.secret && <TbLock />}{" "} <Code>{configVariable.key}</Code>{" "}
<br /> {configVariable.secret && <TbLock />} <br />
<Text size="xs" color="dimmed"> <Text size="xs" color="dimmed">
{element.description} {configVariable.description}
</Text> </Text>
</td> </td>
<td>{element.value}</td> <td>
{configVariable.obscured
? "•".repeat(configVariable.value.length)
: configVariable.value}
</td>
<td> <td>
<Group position="right"> <Group position="right">
<ActionIcon <ActionIcon
@@ -74,7 +77,7 @@ const AdminConfigTable = () => {
onClick={() => onClick={() =>
showUpdateConfigVariableModal( showUpdateConfigVariableModal(
modals, modals,
element, configVariable,
getConfigVariables getConfigVariables
) )
} }

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" &&
(configVariable.obscured ? (
<PasswordInput label="Value" {...form.getInputProps("stringValue")} />
) : (
<TextInput 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

@@ -29,8 +29,8 @@ const showCreateUploadModal = (
modals: ModalsContextProps, modals: ModalsContextProps,
options: { options: {
isUserSignedIn: boolean; isUserSignedIn: boolean;
ALLOW_UNAUTHENTICATED_SHARES: boolean; allowUnauthenticatedShares: boolean;
ENABLE_EMAIL_RECIPIENTS: boolean; enableEmailRecepients: boolean;
}, },
uploadCallback: ( uploadCallback: (
id: string, id: string,
@@ -62,15 +62,13 @@ const CreateUploadModalBody = ({
) => void; ) => void;
options: { options: {
isUserSignedIn: boolean; isUserSignedIn: boolean;
ALLOW_UNAUTHENTICATED_SHARES: boolean; allowUnauthenticatedShares: boolean;
ENABLE_EMAIL_RECIPIENTS: boolean; enableEmailRecepients: boolean;
}; };
}) => { }) => {
const modals = useModals(); const modals = useModals();
const [showNotSignedInAlert, setShowNotSignedInAlert] = useState( const [showNotSignedInAlert, setShowNotSignedInAlert] = useState(true);
options.ENABLE_EMAIL_RECIPIENTS
);
const validationSchema = yup.object().shape({ const validationSchema = yup.object().shape({
link: yup link: yup
@@ -230,7 +228,7 @@ const CreateUploadModalBody = ({
{ExpirationPreview({ form })} {ExpirationPreview({ form })}
</Text> </Text>
<Accordion> <Accordion>
{options.ENABLE_EMAIL_RECIPIENTS && ( {options.enableEmailRecepients && (
<Accordion.Item value="recipients" sx={{ borderBottom: "none" }}> <Accordion.Item value="recipients" sx={{ borderBottom: "none" }}>
<Accordion.Control>Email recipients</Accordion.Control> <Accordion.Control>Email recipients</Accordion.Control>
<Accordion.Panel> <Accordion.Panel>

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

@@ -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,8 +34,7 @@ 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 (
@@ -47,7 +46,7 @@ const Admin = () => {
key={item.title} key={item.title}
className={classes.item} className={classes.item}
> >
<item.icon color={theme.colors.victoria[5]} size={35} /> <item.icon color={theme.colors.victoria[8]} size={35} />
<Text mt={7}>{item.title}</Text> <Text mt={7}>{item.title}</Text>
</Paper> </Paper>
</Col> </Col>
@@ -55,7 +54,6 @@ const Admin = () => {
})} })}
</Grid> </Grid>
</Paper> </Paper>
</Container>
); );
}; };

View File

@@ -105,21 +105,19 @@ const Upload = () => {
<Button <Button
loading={isUploading} loading={isUploading}
disabled={files.length <= 0} disabled={files.length <= 0}
onClick={() => onClick={() => {
showCreateUploadModal( showCreateUploadModal(
modals, modals,
{ {
isUserSignedIn: user ? true : false, isUserSignedIn: user ? true : false,
ALLOW_UNAUTHENTICATED_SHARES: config.get( allowUnauthenticatedShares: config.get(
"ALLOW_UNAUTHENTICATED_SHARES" "ALLOW_UNAUTHENTICATED_SHARES"
), ),
ENABLE_EMAIL_RECIPIENTS: config.get( enableEmailRecepients: config.get("ENABLE_EMAIL_RECIPIENTS"),
"ENABLE_EMAIL_RECIPIENTS"
),
}, },
uploadFiles uploadFiles
) );
} }}
> >
Share Share
</Button> </Button>

View File

@@ -21,7 +21,7 @@ const completeShare = async (id: string) => {
}; };
const get = async (id: string): Promise<Share> => { const get = async (id: string): Promise<Share> => {
const shareToken = localStorage.getItem(`share_${id}_token`); const shareToken = sessionStorage.getItem(`share_${id}_token`);
return ( return (
await api.get(`shares/${id}`, { await api.get(`shares/${id}`, {
headers: { "X-Share-Token": shareToken ?? "" }, headers: { "X-Share-Token": shareToken ?? "" },
@@ -30,7 +30,7 @@ const get = async (id: string): Promise<Share> => {
}; };
const getMetaData = async (id: string): Promise<ShareMetaData> => { const getMetaData = async (id: string): Promise<ShareMetaData> => {
const shareToken = localStorage.getItem(`share_${id}_token`); const shareToken = sessionStorage.getItem(`share_${id}_token`);
return ( return (
await api.get(`shares/${id}/metaData`, { await api.get(`shares/${id}/metaData`, {
headers: { "X-Share-Token": shareToken ?? "" }, headers: { "X-Share-Token": shareToken ?? "" },
@@ -49,7 +49,7 @@ const getMyShares = async (): Promise<MyShare[]> => {
const getShareToken = async (id: string, password?: string) => { const getShareToken = async (id: string, password?: string) => {
const { token } = (await api.post(`/shares/${id}/token`, { password })).data; const { token } = (await api.post(`/shares/${id}/token`, { password })).data;
localStorage.setItem(`share_${id}_token`, token); sessionStorage.setItem(`share_${id}_token`, token);
}; };
const isShareIdAvailable = async (id: string): Promise<boolean> => { const isShareIdAvailable = async (id: string): Promise<boolean> => {
@@ -57,7 +57,7 @@ const isShareIdAvailable = async (id: string): Promise<boolean> => {
}; };
const getFileDownloadUrl = async (shareId: string, fileId: string) => { const getFileDownloadUrl = async (shareId: string, fileId: string) => {
const shareToken = localStorage.getItem(`share_${shareId}_token`); const shareToken = sessionStorage.getItem(`share_${shareId}_token`);
return ( return (
await api.get(`shares/${shareId}/files/${fileId}/download`, { await api.get(`shares/${shareId}/files/${fileId}/download`, {
headers: { "X-Share-Token": shareToken ?? "" }, headers: { "X-Share-Token": shareToken ?? "" },

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.1", "version": "0.3.3",
"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",