Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fccc4cbc02 | ||
|
|
f1b44f87fa | ||
|
|
02e41e2437 | ||
|
|
74e8956106 |
13
CHANGELOG.md
13
CHANGELOG.md
@@ -1,3 +1,16 @@
|
|||||||
|
### [0.6.1](https://github.com/stonith404/pingvin-share/compare/v0.6.0...v0.6.1) (2023-01-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* delete all sessions if password was changed ([02e41e2](https://github.com/stonith404/pingvin-share/commit/02e41e243768de34de1bdc8833e83f60db530e55))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* shareUrl uses wrong origin ([f1b44f8](https://github.com/stonith404/pingvin-share/commit/f1b44f87fa64d3b21ca92c9068cb352d0ad51bc0))
|
||||||
|
* update password doesn't work ([74e8956](https://github.com/stonith404/pingvin-share/commit/74e895610642552c98c0015d0f8347735aaed457))
|
||||||
|
|
||||||
## [0.6.0](https://github.com/stonith404/pingvin-share/compare/v0.5.1...v0.6.0) (2023-01-09)
|
## [0.6.0](https://github.com/stonith404/pingvin-share/compare/v0.5.1...v0.6.0) (2023-01-09)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
4
backend/package-lock.json
generated
4
backend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "pingvin-share-backend",
|
"name": "pingvin-share-backend",
|
||||||
"version": "0.6.0",
|
"version": "0.6.1",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "pingvin-share-backend",
|
"name": "pingvin-share-backend",
|
||||||
"version": "0.6.0",
|
"version": "0.6.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nestjs/common": "^9.2.1",
|
"@nestjs/common": "^9.2.1",
|
||||||
"@nestjs/config": "^2.2.0",
|
"@nestjs/config": "^2.2.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pingvin-share-backend",
|
"name": "pingvin-share-backend",
|
||||||
"version": "0.6.0",
|
"version": "0.6.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "nest build",
|
"build": "nest build",
|
||||||
"dev": "nest start --watch",
|
"dev": "nest start --watch",
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import { AuthRegisterDTO } from "./dto/authRegister.dto";
|
|||||||
import { AuthSignInDTO } from "./dto/authSignIn.dto";
|
import { AuthSignInDTO } from "./dto/authSignIn.dto";
|
||||||
import { AuthSignInTotpDTO } from "./dto/authSignInTotp.dto";
|
import { AuthSignInTotpDTO } from "./dto/authSignInTotp.dto";
|
||||||
import { EnableTotpDTO } from "./dto/enableTotp.dto";
|
import { EnableTotpDTO } from "./dto/enableTotp.dto";
|
||||||
|
import { TokenDTO } from "./dto/token.dto";
|
||||||
import { UpdatePasswordDTO } from "./dto/updatePassword.dto";
|
import { UpdatePasswordDTO } from "./dto/updatePassword.dto";
|
||||||
import { VerifyTotpDTO } from "./dto/verifyTotp.dto";
|
import { VerifyTotpDTO } from "./dto/verifyTotp.dto";
|
||||||
import { JwtGuard } from "./guard/jwt.guard";
|
import { JwtGuard } from "./guard/jwt.guard";
|
||||||
@@ -45,8 +46,8 @@ export class AuthController {
|
|||||||
|
|
||||||
response = this.addTokensToResponse(
|
response = this.addTokensToResponse(
|
||||||
response,
|
response,
|
||||||
result.accessToken,
|
result.refreshToken,
|
||||||
result.refreshToken
|
result.accessToken
|
||||||
);
|
);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -64,8 +65,8 @@ export class AuthController {
|
|||||||
if (result.accessToken && result.refreshToken) {
|
if (result.accessToken && result.refreshToken) {
|
||||||
response = this.addTokensToResponse(
|
response = this.addTokensToResponse(
|
||||||
response,
|
response,
|
||||||
result.accessToken,
|
result.refreshToken,
|
||||||
result.refreshToken
|
result.accessToken
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,17 +84,28 @@ export class AuthController {
|
|||||||
|
|
||||||
response = this.addTokensToResponse(
|
response = this.addTokensToResponse(
|
||||||
response,
|
response,
|
||||||
result.accessToken,
|
result.refreshToken,
|
||||||
result.refreshToken
|
result.accessToken
|
||||||
);
|
);
|
||||||
|
|
||||||
return result;
|
return new TokenDTO().from(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch("password")
|
@Patch("password")
|
||||||
@UseGuards(JwtGuard)
|
@UseGuards(JwtGuard)
|
||||||
async updatePassword(@GetUser() user: User, @Body() dto: UpdatePasswordDTO) {
|
async updatePassword(
|
||||||
await this.authService.updatePassword(user, dto.oldPassword, dto.password);
|
@GetUser() user: User,
|
||||||
|
@Res({ passthrough: true }) response: Response,
|
||||||
|
@Body() dto: UpdatePasswordDTO
|
||||||
|
) {
|
||||||
|
const result = await this.authService.updatePassword(
|
||||||
|
user,
|
||||||
|
dto.oldPassword,
|
||||||
|
dto.password
|
||||||
|
);
|
||||||
|
|
||||||
|
response = this.addTokensToResponse(response, result.refreshToken);
|
||||||
|
return new TokenDTO().from(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post("token")
|
@Post("token")
|
||||||
@@ -108,7 +120,7 @@ export class AuthController {
|
|||||||
request.cookies.refresh_token
|
request.cookies.refresh_token
|
||||||
);
|
);
|
||||||
response.cookie("access_token", accessToken);
|
response.cookie("access_token", accessToken);
|
||||||
return { accessToken };
|
return new TokenDTO().from({ accessToken });
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post("signOut")
|
@Post("signOut")
|
||||||
@@ -146,15 +158,16 @@ export class AuthController {
|
|||||||
|
|
||||||
private addTokensToResponse(
|
private addTokensToResponse(
|
||||||
response: Response,
|
response: Response,
|
||||||
accessToken: string,
|
refreshToken?: string,
|
||||||
refreshToken: string
|
accessToken?: string
|
||||||
) {
|
) {
|
||||||
response.cookie("access_token", accessToken);
|
if (accessToken) response.cookie("access_token", accessToken);
|
||||||
response.cookie("refresh_token", refreshToken, {
|
if (refreshToken)
|
||||||
path: "/api/auth/token",
|
response.cookie("refresh_token", refreshToken, {
|
||||||
httpOnly: true,
|
path: "/api/auth/token",
|
||||||
maxAge: 1000 * 60 * 60 * 24 * 30 * 3,
|
httpOnly: true,
|
||||||
});
|
maxAge: 1000 * 60 * 60 * 24 * 30 * 3,
|
||||||
|
});
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,10 +87,16 @@ export class AuthService {
|
|||||||
|
|
||||||
const hash = await argon.hash(newPassword);
|
const hash = await argon.hash(newPassword);
|
||||||
|
|
||||||
this.prisma.user.update({
|
await this.prisma.refreshToken.deleteMany({
|
||||||
|
where: { userId: user.id },
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.prisma.user.update({
|
||||||
where: { id: user.id },
|
where: { id: user.id },
|
||||||
data: { password: hash },
|
data: { password: hash },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return this.createRefreshToken(user.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async createAccessToken(user: User, refreshTokenId: string) {
|
async createAccessToken(user: User, refreshTokenId: string) {
|
||||||
@@ -112,7 +118,12 @@ export class AuthService {
|
|||||||
refreshTokenId: string;
|
refreshTokenId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
await this.prisma.refreshToken.delete({ where: { id: refreshTokenId } });
|
await this.prisma.refreshToken
|
||||||
|
.delete({ where: { id: refreshTokenId } })
|
||||||
|
.catch((e) => {
|
||||||
|
// Ignore error if refresh token doesn't exist
|
||||||
|
if (e.code != "P2025") throw e;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async refreshAccessToken(refreshToken: string) {
|
async refreshAccessToken(refreshToken: string) {
|
||||||
|
|||||||
15
backend/src/auth/dto/token.dto.ts
Normal file
15
backend/src/auth/dto/token.dto.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { Expose, plainToClass } from "class-transformer";
|
||||||
|
|
||||||
|
export class TokenDTO {
|
||||||
|
@Expose()
|
||||||
|
accessToken: string;
|
||||||
|
|
||||||
|
@Expose()
|
||||||
|
refreshToken: string;
|
||||||
|
|
||||||
|
from(partial: Partial<TokenDTO>) {
|
||||||
|
return plainToClass(TokenDTO, partial, {
|
||||||
|
excludeExtraneousValues: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
4
frontend/package-lock.json
generated
4
frontend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "pingvin-share-frontend",
|
"name": "pingvin-share-frontend",
|
||||||
"version": "0.6.0",
|
"version": "0.6.1",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "pingvin-share-frontend",
|
"name": "pingvin-share-frontend",
|
||||||
"version": "0.6.0",
|
"version": "0.6.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.10.5",
|
"@emotion/react": "^11.10.5",
|
||||||
"@emotion/server": "^11.10.0",
|
"@emotion/server": "^11.10.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pingvin-share-frontend",
|
"name": "pingvin-share-frontend",
|
||||||
"version": "0.6.0",
|
"version": "0.6.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Stack, TextInput } from "@mantine/core";
|
import { Stack, TextInput } from "@mantine/core";
|
||||||
import { ModalsContextProps } from "@mantine/modals/lib/context";
|
import { ModalsContextProps } from "@mantine/modals/lib/context";
|
||||||
|
|
||||||
const showShareLinkModal = (modals: ModalsContextProps, shareId: string) => {
|
const showShareLinkModal = (modals: ModalsContextProps, shareId: string, appUrl : string) => {
|
||||||
const link = `${window.location.origin}/share/${shareId}`;
|
const link = `${appUrl}/share/${shareId}`;
|
||||||
return modals.openModal({
|
return modals.openModal({
|
||||||
title: "Share link",
|
title: "Share link",
|
||||||
children: (
|
children: (
|
||||||
|
|||||||
@@ -15,7 +15,11 @@ import { TbCopy } from "react-icons/tb";
|
|||||||
import { Share } from "../../../types/share.type";
|
import { Share } from "../../../types/share.type";
|
||||||
import toast from "../../../utils/toast.util";
|
import toast from "../../../utils/toast.util";
|
||||||
|
|
||||||
const showCompletedUploadModal = (modals: ModalsContextProps, share: Share) => {
|
const showCompletedUploadModal = (
|
||||||
|
modals: ModalsContextProps,
|
||||||
|
share: Share,
|
||||||
|
appUrl: string
|
||||||
|
) => {
|
||||||
return modals.openModal({
|
return modals.openModal({
|
||||||
closeOnClickOutside: false,
|
closeOnClickOutside: false,
|
||||||
withCloseButton: false,
|
withCloseButton: false,
|
||||||
@@ -25,15 +29,16 @@ const showCompletedUploadModal = (modals: ModalsContextProps, share: Share) => {
|
|||||||
<Title order={4}>Share ready</Title>
|
<Title order={4}>Share ready</Title>
|
||||||
</Stack>
|
</Stack>
|
||||||
),
|
),
|
||||||
children: <Body share={share} />,
|
children: <Body share={share} appUrl={appUrl} />,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const Body = ({ share }: { share: Share }) => {
|
const Body = ({ share, appUrl }: { share: Share; appUrl: string }) => {
|
||||||
const clipboard = useClipboard({ timeout: 500 });
|
const clipboard = useClipboard({ timeout: 500 });
|
||||||
const modals = useModals();
|
const modals = useModals();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const link = `${window.location.origin}/share/${share.id}`;
|
|
||||||
|
const link = `${appUrl}/share/${share.id}`;
|
||||||
return (
|
return (
|
||||||
<Stack align="stretch">
|
<Stack align="stretch">
|
||||||
<TextInput
|
<TextInput
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ const showCreateUploadModal = (
|
|||||||
modals: ModalsContextProps,
|
modals: ModalsContextProps,
|
||||||
options: {
|
options: {
|
||||||
isUserSignedIn: boolean;
|
isUserSignedIn: boolean;
|
||||||
|
appUrl: string;
|
||||||
allowUnauthenticatedShares: boolean;
|
allowUnauthenticatedShares: boolean;
|
||||||
enableEmailRecepients: boolean;
|
enableEmailRecepients: boolean;
|
||||||
},
|
},
|
||||||
@@ -53,6 +54,7 @@ const CreateUploadModalBody = ({
|
|||||||
uploadCallback: (createShare: CreateShare) => void;
|
uploadCallback: (createShare: CreateShare) => void;
|
||||||
options: {
|
options: {
|
||||||
isUserSignedIn: boolean;
|
isUserSignedIn: boolean;
|
||||||
|
appUrl: string;
|
||||||
allowUnauthenticatedShares: boolean;
|
allowUnauthenticatedShares: boolean;
|
||||||
enableEmailRecepients: boolean;
|
enableEmailRecepients: boolean;
|
||||||
};
|
};
|
||||||
@@ -156,7 +158,7 @@ const CreateUploadModalBody = ({
|
|||||||
color: theme.colors.gray[6],
|
color: theme.colors.gray[6],
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{window.location.origin}/share/
|
{options.appUrl}/share/
|
||||||
{form.values.link == "" ? "myAwesomeShare" : form.values.link}
|
{form.values.link == "" ? "myAwesomeShare" : form.values.link}
|
||||||
</Text>
|
</Text>
|
||||||
<Grid align={form.errors.link ? "center" : "flex-end"}>
|
<Grid align={form.errors.link ? "center" : "flex-end"}>
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ 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 showShareLinkModal from "../../components/account/showShareLinkModal";
|
||||||
import Meta from "../../components/Meta";
|
import Meta from "../../components/Meta";
|
||||||
|
import useConfig from "../../hooks/config.hook";
|
||||||
import useUser from "../../hooks/user.hook";
|
import useUser from "../../hooks/user.hook";
|
||||||
import shareService from "../../services/share.service";
|
import shareService from "../../services/share.service";
|
||||||
import { MyShare } from "../../types/share.type";
|
import { MyShare } from "../../types/share.type";
|
||||||
@@ -28,6 +29,8 @@ const MyShares = () => {
|
|||||||
const modals = useModals();
|
const modals = useModals();
|
||||||
const clipboard = useClipboard();
|
const clipboard = useClipboard();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const config = useConfig();
|
||||||
|
|
||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
|
|
||||||
const [shares, setShares] = useState<MyShare[]>();
|
const [shares, setShares] = useState<MyShare[]>();
|
||||||
@@ -86,13 +89,17 @@ const MyShares = () => {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (window.isSecureContext) {
|
if (window.isSecureContext) {
|
||||||
clipboard.copy(
|
clipboard.copy(
|
||||||
`${window.location.origin}/share/${share.id}`
|
`${config.get("APP_URL")}/share/${share.id}`
|
||||||
);
|
);
|
||||||
toast.success(
|
toast.success(
|
||||||
"Your link was copied to the keyboard."
|
"Your link was copied to the keyboard."
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
showShareLinkModal(modals, share.id);
|
showShareLinkModal(
|
||||||
|
modals,
|
||||||
|
share.id,
|
||||||
|
config.get("APP_URL")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ const Upload = () => {
|
|||||||
.completeShare(createdShare.id)
|
.completeShare(createdShare.id)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setisUploading(false);
|
setisUploading(false);
|
||||||
showCompletedUploadModal(modals, createdShare);
|
showCompletedUploadModal(modals, createdShare, config.get("APP_URL"));
|
||||||
setFiles([]);
|
setFiles([]);
|
||||||
})
|
})
|
||||||
.catch(() =>
|
.catch(() =>
|
||||||
@@ -164,6 +164,7 @@ const Upload = () => {
|
|||||||
modals,
|
modals,
|
||||||
{
|
{
|
||||||
isUserSignedIn: user ? true : false,
|
isUserSignedIn: user ? true : false,
|
||||||
|
appUrl: config.get("APP_URL"),
|
||||||
allowUnauthenticatedShares: config.get(
|
allowUnauthenticatedShares: config.get(
|
||||||
"ALLOW_UNAUTHENTICATED_SHARES"
|
"ALLOW_UNAUTHENTICATED_SHARES"
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pingvin-share",
|
"name": "pingvin-share",
|
||||||
"version": "0.6.0",
|
"version": "0.6.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",
|
||||||
|
|||||||
Reference in New Issue
Block a user