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)
|
||||
|
||||
|
||||
|
||||
4
backend/package-lock.json
generated
4
backend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "pingvin-share-backend",
|
||||
"version": "0.6.0",
|
||||
"version": "0.6.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "pingvin-share-backend",
|
||||
"version": "0.6.0",
|
||||
"version": "0.6.1",
|
||||
"dependencies": {
|
||||
"@nestjs/common": "^9.2.1",
|
||||
"@nestjs/config": "^2.2.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pingvin-share-backend",
|
||||
"version": "0.6.0",
|
||||
"version": "0.6.1",
|
||||
"scripts": {
|
||||
"build": "nest build",
|
||||
"dev": "nest start --watch",
|
||||
|
||||
@@ -21,6 +21,7 @@ import { AuthRegisterDTO } from "./dto/authRegister.dto";
|
||||
import { AuthSignInDTO } from "./dto/authSignIn.dto";
|
||||
import { AuthSignInTotpDTO } from "./dto/authSignInTotp.dto";
|
||||
import { EnableTotpDTO } from "./dto/enableTotp.dto";
|
||||
import { TokenDTO } from "./dto/token.dto";
|
||||
import { UpdatePasswordDTO } from "./dto/updatePassword.dto";
|
||||
import { VerifyTotpDTO } from "./dto/verifyTotp.dto";
|
||||
import { JwtGuard } from "./guard/jwt.guard";
|
||||
@@ -45,8 +46,8 @@ export class AuthController {
|
||||
|
||||
response = this.addTokensToResponse(
|
||||
response,
|
||||
result.accessToken,
|
||||
result.refreshToken
|
||||
result.refreshToken,
|
||||
result.accessToken
|
||||
);
|
||||
|
||||
return result;
|
||||
@@ -64,8 +65,8 @@ export class AuthController {
|
||||
if (result.accessToken && result.refreshToken) {
|
||||
response = this.addTokensToResponse(
|
||||
response,
|
||||
result.accessToken,
|
||||
result.refreshToken
|
||||
result.refreshToken,
|
||||
result.accessToken
|
||||
);
|
||||
}
|
||||
|
||||
@@ -83,17 +84,28 @@ export class AuthController {
|
||||
|
||||
response = this.addTokensToResponse(
|
||||
response,
|
||||
result.accessToken,
|
||||
result.refreshToken
|
||||
result.refreshToken,
|
||||
result.accessToken
|
||||
);
|
||||
|
||||
return result;
|
||||
return new TokenDTO().from(result);
|
||||
}
|
||||
|
||||
@Patch("password")
|
||||
@UseGuards(JwtGuard)
|
||||
async updatePassword(@GetUser() user: User, @Body() dto: UpdatePasswordDTO) {
|
||||
await this.authService.updatePassword(user, dto.oldPassword, dto.password);
|
||||
async updatePassword(
|
||||
@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")
|
||||
@@ -108,7 +120,7 @@ export class AuthController {
|
||||
request.cookies.refresh_token
|
||||
);
|
||||
response.cookie("access_token", accessToken);
|
||||
return { accessToken };
|
||||
return new TokenDTO().from({ accessToken });
|
||||
}
|
||||
|
||||
@Post("signOut")
|
||||
@@ -146,15 +158,16 @@ export class AuthController {
|
||||
|
||||
private addTokensToResponse(
|
||||
response: Response,
|
||||
accessToken: string,
|
||||
refreshToken: string
|
||||
refreshToken?: string,
|
||||
accessToken?: string
|
||||
) {
|
||||
response.cookie("access_token", accessToken);
|
||||
response.cookie("refresh_token", refreshToken, {
|
||||
path: "/api/auth/token",
|
||||
httpOnly: true,
|
||||
maxAge: 1000 * 60 * 60 * 24 * 30 * 3,
|
||||
});
|
||||
if (accessToken) response.cookie("access_token", accessToken);
|
||||
if (refreshToken)
|
||||
response.cookie("refresh_token", refreshToken, {
|
||||
path: "/api/auth/token",
|
||||
httpOnly: true,
|
||||
maxAge: 1000 * 60 * 60 * 24 * 30 * 3,
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -87,10 +87,16 @@ export class AuthService {
|
||||
|
||||
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 },
|
||||
data: { password: hash },
|
||||
});
|
||||
|
||||
return this.createRefreshToken(user.id);
|
||||
}
|
||||
|
||||
async createAccessToken(user: User, refreshTokenId: string) {
|
||||
@@ -112,7 +118,12 @@ export class AuthService {
|
||||
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) {
|
||||
|
||||
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",
|
||||
"version": "0.6.0",
|
||||
"version": "0.6.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "pingvin-share-frontend",
|
||||
"version": "0.6.0",
|
||||
"version": "0.6.1",
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.10.5",
|
||||
"@emotion/server": "^11.10.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pingvin-share-frontend",
|
||||
"version": "0.6.0",
|
||||
"version": "0.6.1",
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
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}`;
|
||||
const showShareLinkModal = (modals: ModalsContextProps, shareId: string, appUrl : string) => {
|
||||
const link = `${appUrl}/share/${shareId}`;
|
||||
return modals.openModal({
|
||||
title: "Share link",
|
||||
children: (
|
||||
|
||||
@@ -15,7 +15,11 @@ import { TbCopy } from "react-icons/tb";
|
||||
import { Share } from "../../../types/share.type";
|
||||
import toast from "../../../utils/toast.util";
|
||||
|
||||
const showCompletedUploadModal = (modals: ModalsContextProps, share: Share) => {
|
||||
const showCompletedUploadModal = (
|
||||
modals: ModalsContextProps,
|
||||
share: Share,
|
||||
appUrl: string
|
||||
) => {
|
||||
return modals.openModal({
|
||||
closeOnClickOutside: false,
|
||||
withCloseButton: false,
|
||||
@@ -25,15 +29,16 @@ const showCompletedUploadModal = (modals: ModalsContextProps, share: Share) => {
|
||||
<Title order={4}>Share ready</Title>
|
||||
</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 modals = useModals();
|
||||
const router = useRouter();
|
||||
const link = `${window.location.origin}/share/${share.id}`;
|
||||
|
||||
const link = `${appUrl}/share/${share.id}`;
|
||||
return (
|
||||
<Stack align="stretch">
|
||||
<TextInput
|
||||
|
||||
@@ -30,6 +30,7 @@ const showCreateUploadModal = (
|
||||
modals: ModalsContextProps,
|
||||
options: {
|
||||
isUserSignedIn: boolean;
|
||||
appUrl: string;
|
||||
allowUnauthenticatedShares: boolean;
|
||||
enableEmailRecepients: boolean;
|
||||
},
|
||||
@@ -53,6 +54,7 @@ const CreateUploadModalBody = ({
|
||||
uploadCallback: (createShare: CreateShare) => void;
|
||||
options: {
|
||||
isUserSignedIn: boolean;
|
||||
appUrl: string;
|
||||
allowUnauthenticatedShares: boolean;
|
||||
enableEmailRecepients: boolean;
|
||||
};
|
||||
@@ -156,7 +158,7 @@ const CreateUploadModalBody = ({
|
||||
color: theme.colors.gray[6],
|
||||
})}
|
||||
>
|
||||
{window.location.origin}/share/
|
||||
{options.appUrl}/share/
|
||||
{form.values.link == "" ? "myAwesomeShare" : form.values.link}
|
||||
</Text>
|
||||
<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 showShareLinkModal from "../../components/account/showShareLinkModal";
|
||||
import Meta from "../../components/Meta";
|
||||
import useConfig from "../../hooks/config.hook";
|
||||
import useUser from "../../hooks/user.hook";
|
||||
import shareService from "../../services/share.service";
|
||||
import { MyShare } from "../../types/share.type";
|
||||
@@ -28,6 +29,8 @@ const MyShares = () => {
|
||||
const modals = useModals();
|
||||
const clipboard = useClipboard();
|
||||
const router = useRouter();
|
||||
const config = useConfig();
|
||||
|
||||
const { user } = useUser();
|
||||
|
||||
const [shares, setShares] = useState<MyShare[]>();
|
||||
@@ -86,13 +89,17 @@ const MyShares = () => {
|
||||
onClick={() => {
|
||||
if (window.isSecureContext) {
|
||||
clipboard.copy(
|
||||
`${window.location.origin}/share/${share.id}`
|
||||
`${config.get("APP_URL")}/share/${share.id}`
|
||||
);
|
||||
toast.success(
|
||||
"Your link was copied to the keyboard."
|
||||
);
|
||||
} else {
|
||||
showShareLinkModal(modals, share.id);
|
||||
showShareLinkModal(
|
||||
modals,
|
||||
share.id,
|
||||
config.get("APP_URL")
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -140,7 +140,7 @@ const Upload = () => {
|
||||
.completeShare(createdShare.id)
|
||||
.then(() => {
|
||||
setisUploading(false);
|
||||
showCompletedUploadModal(modals, createdShare);
|
||||
showCompletedUploadModal(modals, createdShare, config.get("APP_URL"));
|
||||
setFiles([]);
|
||||
})
|
||||
.catch(() =>
|
||||
@@ -164,6 +164,7 @@ const Upload = () => {
|
||||
modals,
|
||||
{
|
||||
isUserSignedIn: user ? true : false,
|
||||
appUrl: config.get("APP_URL"),
|
||||
allowUnauthenticatedShares: config.get(
|
||||
"ALLOW_UNAUTHENTICATED_SHARES"
|
||||
),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pingvin-share",
|
||||
"version": "0.6.0",
|
||||
"version": "0.6.1",
|
||||
"scripts": {
|
||||
"format": "cd frontend && npm run format && cd ../backend && npm run format",
|
||||
"lint": "cd frontend && npm run lint && cd ../backend && npm run lint",
|
||||
|
||||
Reference in New Issue
Block a user