feat: allow multiple shares with one reverse share link
This commit is contained in:
@@ -47,6 +47,7 @@ const Body = ({
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
maxShareSize: 104857600,
|
||||
maxUseCount: 1,
|
||||
sendEmailNotification: false,
|
||||
expiration_num: 1,
|
||||
expiration_unit: "-days",
|
||||
@@ -60,6 +61,7 @@ const Body = ({
|
||||
.createReverseShare(
|
||||
values.expiration_num + values.expiration_unit,
|
||||
values.maxShareSize,
|
||||
values.maxUseCount,
|
||||
values.sendEmailNotification
|
||||
)
|
||||
.then(({ link }) => {
|
||||
@@ -132,6 +134,15 @@ const Body = ({
|
||||
value={form.values.maxShareSize}
|
||||
onChange={(number) => form.setFieldValue("maxShareSize", number)}
|
||||
/>
|
||||
<NumberInput
|
||||
min={1}
|
||||
max={1000}
|
||||
precision={0}
|
||||
variant="filled"
|
||||
label="Max use count"
|
||||
description="The maximum number of times this reverse share link can be used"
|
||||
{...form.getInputProps("maxUseCount")}
|
||||
/>
|
||||
{showSendEmailNotificationOption && (
|
||||
<Switch
|
||||
mt="xs"
|
||||
|
||||
@@ -13,12 +13,12 @@ export const config = {
|
||||
|
||||
export async function middleware(request: NextRequest) {
|
||||
const routes = {
|
||||
unauthenticated: new Routes(["/auth/signIn", "/auth/resetPassword*", "/"]),
|
||||
unauthenticated: new Routes(["/auth/*", "/"]),
|
||||
public: new Routes(["/share/*", "/upload/*"]),
|
||||
setupStatusRegistered: new Routes(["/auth/*", "/admin/setup"]),
|
||||
admin: new Routes(["/admin/*"]),
|
||||
account: new Routes(["/account/*"]),
|
||||
disabledRoutes: new Routes([]),
|
||||
disabled: new Routes([]),
|
||||
};
|
||||
|
||||
// Get config from backend
|
||||
@@ -46,7 +46,7 @@ export async function middleware(request: NextRequest) {
|
||||
}
|
||||
|
||||
if (!getConfig("ALLOW_REGISTRATION")) {
|
||||
routes.disabledRoutes.routes.push("/auth/signUp");
|
||||
routes.disabled.routes.push("/auth/signUp");
|
||||
}
|
||||
|
||||
if (getConfig("ALLOW_UNAUTHENTICATED_SHARES")) {
|
||||
@@ -54,14 +54,14 @@ export async function middleware(request: NextRequest) {
|
||||
}
|
||||
|
||||
if (!getConfig("SMTP_ENABLED")) {
|
||||
routes.disabledRoutes.routes.push("/auth/resetPassword*");
|
||||
routes.disabled.routes.push("/auth/resetPassword*");
|
||||
}
|
||||
|
||||
// prettier-ignore
|
||||
const rules = [
|
||||
// Disabled routes
|
||||
{
|
||||
condition: routes.disabledRoutes.contains(route),
|
||||
condition: routes.disabled.contains(route),
|
||||
path: "/",
|
||||
},
|
||||
// Setup status
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
Accordion,
|
||||
ActionIcon,
|
||||
Box,
|
||||
Button,
|
||||
@@ -54,7 +55,7 @@ const MyShares = () => {
|
||||
position="bottom"
|
||||
multiline
|
||||
width={220}
|
||||
label="A reverse share allows you to generate a unique URL for a single-use share for an external user."
|
||||
label="A reverse share allows you to generate a unique URL that allows external users to create a share."
|
||||
events={{ hover: true, focus: false, touch: true }}
|
||||
>
|
||||
<ActionIcon>
|
||||
@@ -87,8 +88,8 @@ const MyShares = () => {
|
||||
<Table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Visitors</th>
|
||||
<th>Shares</th>
|
||||
<th>Remaining uses</th>
|
||||
<th>Max share size</th>
|
||||
<th>Expires at</th>
|
||||
<th></th>
|
||||
@@ -97,14 +98,63 @@ const MyShares = () => {
|
||||
<tbody>
|
||||
{reverseShares.map((reverseShare) => (
|
||||
<tr key={reverseShare.id}>
|
||||
<td>
|
||||
{reverseShare.share ? (
|
||||
reverseShare.share?.id
|
||||
<td style={{ width: 220 }}>
|
||||
{reverseShare.shares.length == 0 ? (
|
||||
<Text color="dimmed" size="sm">
|
||||
No shares created yet
|
||||
</Text>
|
||||
) : (
|
||||
<Text color="dimmed">No share created yet</Text>
|
||||
<Accordion>
|
||||
<Accordion.Item
|
||||
value="customization"
|
||||
sx={{ borderBottom: "none" }}
|
||||
>
|
||||
<Accordion.Control p={0}>
|
||||
<Text size="sm">
|
||||
{`${reverseShare.shares.length} share${
|
||||
reverseShare.shares.length > 1 ? "s" : ""
|
||||
}`}
|
||||
</Text>
|
||||
</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
{reverseShare.shares.map((share) => (
|
||||
<Group key={share.id} mb={4}>
|
||||
<Text maw={120} truncate>
|
||||
{share.id}
|
||||
</Text>
|
||||
<ActionIcon
|
||||
color="victoria"
|
||||
variant="light"
|
||||
size={25}
|
||||
onClick={() => {
|
||||
if (window.isSecureContext) {
|
||||
clipboard.copy(
|
||||
`${config.get("APP_URL")}/share/${
|
||||
share.id
|
||||
}`
|
||||
);
|
||||
toast.success(
|
||||
"The share link was copied to the keyboard."
|
||||
);
|
||||
} else {
|
||||
showShareLinkModal(
|
||||
modals,
|
||||
share.id,
|
||||
config.get("APP_URL")
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<TbLink />
|
||||
</ActionIcon>
|
||||
</Group>
|
||||
))}
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
</Accordion>
|
||||
)}
|
||||
</td>
|
||||
<td>{reverseShare.share?.views ?? "0"}</td>
|
||||
<td>{reverseShare.remainingUses}</td>
|
||||
<td>
|
||||
{byteToHumanSizeString(parseInt(reverseShare.maxShareSize))}
|
||||
</td>
|
||||
@@ -115,33 +165,6 @@ const MyShares = () => {
|
||||
</td>
|
||||
<td>
|
||||
<Group position="right">
|
||||
{reverseShare.share && (
|
||||
<ActionIcon
|
||||
color="victoria"
|
||||
variant="light"
|
||||
size={25}
|
||||
onClick={() => {
|
||||
if (window.isSecureContext) {
|
||||
clipboard.copy(
|
||||
`${config.get("APP_URL")}/share/${
|
||||
reverseShare.share!.id
|
||||
}`
|
||||
);
|
||||
toast.success(
|
||||
"The share link was copied to the keyboard."
|
||||
);
|
||||
} else {
|
||||
showShareLinkModal(
|
||||
modals,
|
||||
reverseShare.share!.id,
|
||||
config.get("APP_URL")
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<TbLink />
|
||||
</ActionIcon>
|
||||
)}
|
||||
<ActionIcon
|
||||
color="red"
|
||||
variant="light"
|
||||
@@ -152,13 +175,14 @@ const MyShares = () => {
|
||||
children: (
|
||||
<Text size="sm">
|
||||
Do you really want to delete this reverse share?
|
||||
If you do, the share will be deleted as well.
|
||||
If you do, the associated shares will be deleted
|
||||
as well.
|
||||
</Text>
|
||||
),
|
||||
confirmProps: {
|
||||
color: "red",
|
||||
},
|
||||
labels: { confirm: "Confirm", cancel: "Cancel" },
|
||||
labels: { confirm: "Delete", cancel: "Cancel" },
|
||||
onConfirm: () => {
|
||||
shareService.removeReverseShare(reverseShare.id);
|
||||
setReverseShares(
|
||||
|
||||
@@ -99,12 +99,14 @@ const uploadFile = async (
|
||||
const createReverseShare = async (
|
||||
shareExpiration: string,
|
||||
maxShareSize: number,
|
||||
maxUseCount: number,
|
||||
sendEmailNotification: boolean
|
||||
) => {
|
||||
return (
|
||||
await api.post("reverseShares", {
|
||||
shareExpiration,
|
||||
maxShareSize: maxShareSize.toString(),
|
||||
maxUseCount,
|
||||
sendEmailNotification,
|
||||
})
|
||||
).data;
|
||||
|
||||
@@ -31,7 +31,8 @@ export type MyReverseShare = {
|
||||
id: string;
|
||||
maxShareSize: string;
|
||||
shareExpiration: Date;
|
||||
share?: MyShare;
|
||||
remainingUses: number;
|
||||
shares: MyShare[];
|
||||
};
|
||||
|
||||
export type ShareSecurity = {
|
||||
|
||||
Reference in New Issue
Block a user