From 3d05d22e90405f9ab455d6c0403a68bc50c540c6 Mon Sep 17 00:00:00 2001 From: Dustin Healy <54083382+dustinhealy@users.noreply.github.com> Date: Tue, 2 Dec 2025 12:58:01 -0800 Subject: [PATCH] wip: shared links ttl prototype --- .env.example | 2 + api/server/routes/share.js | 9 +- .../ConvoOptions/SharedLinkButton.tsx | 41 +++++++-- .../Nav/SettingsTabs/Data/SharedLinks.tsx | 22 +++++ client/src/data-provider/mutations.ts | 16 +++- client/src/locales/en/translation.json | 8 ++ client/src/utils/shareExpiry.ts | 32 +++++++ packages/data-provider/src/data-service.ts | 6 +- packages/data-schemas/src/methods/share.ts | 15 ++++ packages/data-schemas/src/schema/share.ts | 6 ++ packages/data-schemas/src/types/share.ts | 3 + .../data-schemas/src/utils/shareExpiration.ts | 87 +++++++++++++++++++ 12 files changed, 234 insertions(+), 13 deletions(-) create mode 100644 client/src/utils/shareExpiry.ts create mode 100644 packages/data-schemas/src/utils/shareExpiration.ts diff --git a/.env.example b/.env.example index 90995be72f..5f3b857377 100644 --- a/.env.example +++ b/.env.example @@ -623,6 +623,8 @@ AZURE_CONTAINER_NAME=files ALLOW_SHARED_LINKS=true ALLOW_SHARED_LINKS_PUBLIC=true +# Default expiration time for shared links in hours (default: 0 = never expires) +# SHARED_LINK_DEFAULT_TTL_HOURS=0 #==============================# # Static File Cache Control # diff --git a/api/server/routes/share.js b/api/server/routes/share.js index 6400b8b637..7f72f60e93 100644 --- a/api/server/routes/share.js +++ b/api/server/routes/share.js @@ -99,8 +99,13 @@ router.get('/link/:conversationId', requireJwtAuth, async (req, res) => { router.post('/:conversationId', requireJwtAuth, async (req, res) => { try { - const { targetMessageId } = req.body; - const created = await createSharedLink(req.user.id, req.params.conversationId, targetMessageId); + const { targetMessageId, expirationHours } = req.body; + const created = await createSharedLink( + req.user.id, + req.params.conversationId, + targetMessageId, + expirationHours, + ); if (created) { res.status(200).json(created); } else { diff --git a/client/src/components/Conversations/ConvoOptions/SharedLinkButton.tsx b/client/src/components/Conversations/ConvoOptions/SharedLinkButton.tsx index e5d1dbfb20..ad475651cb 100644 --- a/client/src/components/Conversations/ConvoOptions/SharedLinkButton.tsx +++ b/client/src/components/Conversations/ConvoOptions/SharedLinkButton.tsx @@ -6,6 +6,7 @@ import { Spinner, TooltipAnchor, Label, + Dropdown, OGDialogTemplate, useToastContext, } from '@librechat/client'; @@ -17,6 +18,7 @@ import { } from '~/data-provider'; import { NotificationSeverity } from '~/common'; import { useLocalize } from '~/hooks'; +import { SHARE_EXPIRY } from '~/utils/shareExpiry'; export default function SharedLinkButton({ share, @@ -38,8 +40,12 @@ export default function SharedLinkButton({ const localize = useLocalize(); const { showToast } = useToastContext(); const [showDeleteDialog, setShowDeleteDialog] = useState(false); + const [expiryLabel, setExpiryLabel] = useState(SHARE_EXPIRY.THIRTY_DAYS.label); const shareId = share?.shareId ?? ''; + const expirationOptions = Object.values(SHARE_EXPIRY); + const localizedOptions = expirationOptions.map((option) => localize(option.label)); + const { mutateAsync: mutate, isLoading: isCreateLoading } = useCreateSharedLinkMutation({ onError: () => { showToast({ @@ -88,7 +94,9 @@ export default function SharedLinkButton({ }; const createShareLink = async () => { - const share = await mutate({ conversationId, targetMessageId }); + const selectedOption = expirationOptions.find((option) => option.label === expiryLabel); + const expirationHours = selectedOption ? selectedOption.hours : undefined; + const share = await mutate({ conversationId, targetMessageId, expirationHours }); const newLink = generateShareLink(share.shareId); setSharedLink(newLink); }; @@ -117,12 +125,33 @@ export default function SharedLinkButton({ return ( <> -