mirror of
https://github.com/DrizzleTime/Foxel.git
synced 2026-09-05 15:47:00 +08:00
feat: add expired share cleanup functionality
This commit is contained in:
@@ -83,6 +83,18 @@ async def get_my_shares(current_user: User = Depends(get_current_active_user)):
|
|||||||
return [ShareInfo.from_orm(s) for s in shares]
|
return [ShareInfo.from_orm(s) for s in shares]
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete("/expired")
|
||||||
|
async def delete_expired_shares(
|
||||||
|
current_user: User = Depends(get_current_active_user),
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
删除当前用户的所有已过期分享。
|
||||||
|
"""
|
||||||
|
user_account = await UserAccount.get(id=current_user.id)
|
||||||
|
deleted_count = await share_service.delete_expired_shares(user=user_account)
|
||||||
|
return success({"deleted_count": deleted_count})
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{share_id}")
|
@router.delete("/{share_id}")
|
||||||
async def delete_share(
|
async def delete_share(
|
||||||
share_id: int,
|
share_id: int,
|
||||||
|
|||||||
@@ -90,6 +90,16 @@ class ShareService:
|
|||||||
raise HTTPException(status_code=404, detail="分享链接不存在")
|
raise HTTPException(status_code=404, detail="分享链接不存在")
|
||||||
await share.delete()
|
await share.delete()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def delete_expired_shares(user: UserAccount) -> int:
|
||||||
|
"""
|
||||||
|
删除当前用户所有已过期的分享链接,返回删除数量。
|
||||||
|
条件:expires_at 非空 且 小于等于当前时间(UTC)。
|
||||||
|
"""
|
||||||
|
now = datetime.now(timezone.utc)
|
||||||
|
deleted_count = await ShareLink.filter(user=user, expires_at__lte=now).delete()
|
||||||
|
return deleted_count
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def get_shared_item_details(share: ShareLink, sub_path: str = ""):
|
async def get_shared_item_details(share: ShareLink, sub_path: str = ""):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -23,10 +23,15 @@ export interface ShareCreatePayload {
|
|||||||
password?: string;
|
password?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ClearExpiredResult {
|
||||||
|
deleted_count: number;
|
||||||
|
}
|
||||||
|
|
||||||
export const shareApi = {
|
export const shareApi = {
|
||||||
create: (payload: ShareCreatePayload) => request<ShareInfoWithPassword>('/shares', { method: 'POST', json: payload }),
|
create: (payload: ShareCreatePayload) => request<ShareInfoWithPassword>('/shares', { method: 'POST', json: payload }),
|
||||||
list: () => request<ShareInfo[]>('/shares'),
|
list: () => request<ShareInfo[]>('/shares'),
|
||||||
remove: (shareId: number) => request<void>(`/shares/${shareId}`, { method: 'DELETE' }),
|
remove: (shareId: number) => request<void>(`/shares/${shareId}`, { method: 'DELETE' }),
|
||||||
|
clearExpired: () => request<ClearExpiredResult>(`/shares/expired`, { method: 'DELETE' }),
|
||||||
get: (token: string) => request<ShareInfo>(`/s/${token}`),
|
get: (token: string) => request<ShareInfo>(`/s/${token}`),
|
||||||
verifyPassword: (token: string, password: string) => request<void>(`/s/${token}/verify`, { method: 'POST', json: { password } }),
|
verifyPassword: (token: string, password: string) => request<void>(`/s/${token}/verify`, { method: 'POST', json: { password } }),
|
||||||
listDir: (token: string, path: string = '/', password?: string) => {
|
listDir: (token: string, path: string = '/', password?: string) => {
|
||||||
|
|||||||
@@ -53,6 +53,9 @@ export const en = {
|
|||||||
'Cancel failed': 'Cancel failed',
|
'Cancel failed': 'Cancel failed',
|
||||||
'Load failed': 'Load failed',
|
'Load failed': 'Load failed',
|
||||||
'Are you sure to cancel share?': 'Are you sure to cancel share?',
|
'Are you sure to cancel share?': 'Are you sure to cancel share?',
|
||||||
|
'Clear expired shares': 'Clear expired shares',
|
||||||
|
'Confirm clear expired shares?': 'Confirm clear expired shares?',
|
||||||
|
'Cleared {count} expired shares': 'Cleared {count} expired shares',
|
||||||
|
|
||||||
'Share Name': 'Share Name',
|
'Share Name': 'Share Name',
|
||||||
'Share Content': 'Share Content',
|
'Share Content': 'Share Content',
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export const zh = {
|
|||||||
'Account Settings': '账户设置',
|
'Account Settings': '账户设置',
|
||||||
'Language': '语言',
|
'Language': '语言',
|
||||||
'Chinese': '中文',
|
'Chinese': '中文',
|
||||||
'English': '英文',
|
'English': 'English',
|
||||||
'Full Name': '昵称',
|
'Full Name': '昵称',
|
||||||
'Email': '邮箱',
|
'Email': '邮箱',
|
||||||
'Change Password': '修改密码',
|
'Change Password': '修改密码',
|
||||||
@@ -57,6 +57,9 @@ export const zh = {
|
|||||||
'Cancel failed': '取消失败',
|
'Cancel failed': '取消失败',
|
||||||
'Load failed': '加载失败',
|
'Load failed': '加载失败',
|
||||||
'Are you sure to cancel share?': '确认取消分享?',
|
'Are you sure to cancel share?': '确认取消分享?',
|
||||||
|
'Clear expired shares': '清空过期分享',
|
||||||
|
'Confirm clear expired shares?': '确认清空过期分享?',
|
||||||
|
'Cleared {count} expired shares': '已清理 {count} 个过期分享',
|
||||||
'Share Name': '分享名称',
|
'Share Name': '分享名称',
|
||||||
'Share Content': '分享内容',
|
'Share Content': '分享内容',
|
||||||
'Created At': '创建时间',
|
'Created At': '创建时间',
|
||||||
|
|||||||
@@ -44,6 +44,16 @@ const SharePage = memo(function SharePage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleClearExpired = async () => {
|
||||||
|
try {
|
||||||
|
const res = await shareApi.clearExpired();
|
||||||
|
message.success(t('Cleared {count} expired shares', { count: String(res.deleted_count) }));
|
||||||
|
fetchList();
|
||||||
|
} catch (e: any) {
|
||||||
|
message.error(e.message || t('Clear failed'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: t('Share Name'),
|
title: t('Share Name'),
|
||||||
@@ -100,7 +110,14 @@ const SharePage = memo(function SharePage() {
|
|||||||
return (
|
return (
|
||||||
<PageCard
|
<PageCard
|
||||||
title={t('My Shares')}
|
title={t('My Shares')}
|
||||||
extra={<Button onClick={fetchList} loading={loading}>{t('Refresh')}</Button>}
|
extra={
|
||||||
|
<Space>
|
||||||
|
<Button onClick={fetchList} loading={loading}>{t('Refresh')}</Button>
|
||||||
|
<Popconfirm title={t('Confirm clear expired shares?')} onConfirm={handleClearExpired}>
|
||||||
|
<Button danger>{t('Clear expired shares')}</Button>
|
||||||
|
</Popconfirm>
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Table
|
<Table
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
|
|||||||
Reference in New Issue
Block a user