From 3461c361f8c1f44a9300e015fd15e03fe730866b Mon Sep 17 00:00:00 2001 From: ttipo Date: Sat, 31 Oct 2026 00:37:55 +0900 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20=EB=8C=80=EC=9A=A9=EB=9F=89?= =?UTF-8?q?=20=EC=B2=A8=EB=B6=80=20=E2=80=94=20=EC=93=B0=EA=B8=B0=20=C2=B7?= =?UTF-8?q?=20=EC=9D=BD=EA=B8=B0=20=C2=B7=20=EC=84=A4=EC=A0=95=20=C2=B7=20?= =?UTF-8?q?=EA=B3=B5=EA=B0=9C=20=EB=82=B4=EB=A0=A4=EB=B0=9B=EA=B8=B0=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 쓰기: 20MB 넘는 파일 · 합계 25MB 를 넘기는 파일 · 메일에 못 담는 형식은 저절로 링크로(묻는 창 없음). 고르자마자 서버에 만들고 16MiB 조각(Content-Digest)을 파일 3개까지 함께, 끊기면 2 · 5 · 15 · 30초로 다시 · 1분 넘으면 멈춤 + [다시 시도], 새로고침 뒤 같은 파일을 다시 고르면 잇는다. 다 올릴 때까지 [보내기]는 aria-disabled + 까닭, 위험 판정은 빨간 줄 + 실시간 알림. 대용량 파일이 있으면 첫 원문 인용 앞을 data-ara-quote-start · bodyQuoteStart 로 알린다(서버가 그 자리에 덩이를 넣는다). - 읽기: 첨부 칸의 대용량 항목(서버의 largeFiles), 본문 덩이 접기, 보낸 사람의 받은 수 · 링크 끊기(끊은 날 — revokedAt). - 설정 › 메일 › 대용량 첨부: 보관 사용량 · 보내지 않은 파일 · 보낸 링크(끊기). - 공개 화면 /s/:token: 세션 복원 없이(meta.public) · credentials omit · no-referrer, 라우트는 대소문자를 가린다. Co-Authored-By: Claude Opus 5.5 --- frontend/src/App.vue | 12 +- .../mail/ComposeAttachmentGroups.vue | 202 ++++ .../components/mail/ComposeAttachmentList.vue | 27 +- .../src/components/mail/ComposeFooter.spec.ts | 82 ++ .../src/components/mail/ComposeFooter.vue | 118 ++- .../mail/ComposeLargeFileItem.spec.ts | 67 ++ .../components/mail/ComposeLargeFileItem.vue | 519 ++++++++++ .../components/mail/ComposeLeaveDialog.vue | 10 +- .../components/mail/LargeFileRevokeDialog.vue | 67 ++ .../src/components/mail/MailBodyFrame.vue | 16 +- .../MessageAttachments.large-files.spec.ts | 215 ++++ .../components/mail/MessageAttachments.vue | 89 +- .../components/mail/MessageLargeFileItem.vue | 198 ++++ .../mail/MessageLargeFileLinkBar.vue | 162 +++ .../src/components/mail/MessageReader.vue | 7 +- .../components/mail/editor/MailEditor.spec.ts | 4 +- .../src/components/mail/editor/MailEditor.vue | 38 +- .../components/mail/editor/fit-draft.spec.ts | 60 +- .../src/components/mail/editor/fit-draft.ts | 4 +- .../mail/editor/mail-editor-handle.ts | 16 +- .../components/mail/editor/mail-extensions.ts | 15 +- .../mail/editor/mail-serialize.spec.ts | 49 + .../components/mail/editor/mail-serialize.ts | 56 +- .../mail/editor/signature/SignatureEditor.vue | 6 +- .../settings/LargeFileDeleteDialog.vue | 62 ++ .../components/settings/LargeFileLinkList.vue | 529 ++++++++++ .../settings/LargeFileUnsentList.vue | 324 ++++++ .../components/settings/LargeFileUsage.vue | 145 +++ .../src/components/share/ShareFileRow.vue | 234 +++++ frontend/src/composables/useApi.spec.ts | 6 + .../src/composables/useAttachmentUploads.ts | 7 + .../src/composables/useComposeLargeFiles.ts | 267 +++++ .../src/composables/useLargeFileAlerts.ts | 26 + .../useLargeFileScanRefresh.spec.ts | 135 +++ .../composables/useLargeFileScanRefresh.ts | 100 ++ .../composables/useLargeFileUploads.spec.ts | 664 ++++++++++++ .../src/composables/useLargeFileUploads.ts | 979 ++++++++++++++++++ frontend/src/composables/useLargeFiles.ts | 145 +++ frontend/src/composables/useMail.ts | 3 +- frontend/src/composables/useMailRealtime.ts | 16 +- frontend/src/composables/usePublicShare.ts | 91 ++ .../src/pages/mail/ComposePage.drafts.spec.ts | 4 +- .../mail/ComposePage.editor-load.spec.ts | 4 +- .../mail/ComposePage.large-files.spec.ts | 470 +++++++++ frontend/src/pages/mail/ComposePage.spec.ts | 43 +- frontend/src/pages/mail/ComposePage.vue | 167 ++- frontend/src/pages/mail/MailLayout.vue | 2 + .../pages/settings/MailLargeFilesPage.spec.ts | 354 +++++++ .../src/pages/settings/MailLargeFilesPage.vue | 152 +++ .../src/pages/settings/SettingsLayout.spec.ts | 23 +- frontend/src/pages/share/SharePage.spec.ts | 319 ++++++ frontend/src/pages/share/SharePage.vue | 640 ++++++++++++ frontend/src/router/index.spec.ts | 44 + frontend/src/router/index.ts | 7 + frontend/src/router/routes.ts | 21 +- frontend/src/shared/constants/icons.ts | 12 + frontend/src/shared/constants/large-file.ts | 333 ++++++ .../src/shared/constants/settings-menu.ts | 5 +- frontend/src/shared/types/large-file.ts | 179 ++++ frontend/src/shared/types/mail.ts | 21 + frontend/src/shared/utils/compose-changes.ts | 8 +- frontend/src/shared/utils/format.spec.ts | 26 + frontend/src/shared/utils/format.ts | 28 + frontend/src/shared/utils/kst-date.spec.ts | 50 + frontend/src/shared/utils/kst-date.ts | 95 ++ .../shared/utils/large-file-status.spec.ts | 92 ++ .../src/shared/utils/large-file-status.ts | 108 ++ frontend/src/shared/utils/large-file.spec.ts | 223 ++++ frontend/src/shared/utils/large-file.ts | 313 ++++++ frontend/src/shared/utils/mail-events.spec.ts | 12 + frontend/src/shared/utils/mail-events.ts | 42 +- frontend/src/shared/utils/mail-html.spec.ts | 46 + frontend/src/shared/utils/mail-html.ts | 66 +- .../src/shared/utils/mail-realtime.spec.ts | 2 + frontend/src/shared/utils/mail-realtime.ts | 8 + .../shared/utils/outgoing-html-rules.spec.ts | 19 +- .../src/shared/utils/outgoing-html-rules.ts | 24 + frontend/src/shared/utils/share-links.spec.ts | 71 ++ frontend/src/shared/utils/share-links.ts | 105 ++ frontend/src/stores/large-files.store.ts | 232 +++++ frontend/src/stores/mail.store.ts | 2 + frontend/src/stores/mail/message.ts | 50 +- .../src/test-utils/outgoing-style-samples.ts | 19 + frontend/src/test-utils/router.ts | 8 + 84 files changed, 10084 insertions(+), 137 deletions(-) create mode 100644 frontend/src/components/mail/ComposeAttachmentGroups.vue create mode 100644 frontend/src/components/mail/ComposeLargeFileItem.spec.ts create mode 100644 frontend/src/components/mail/ComposeLargeFileItem.vue create mode 100644 frontend/src/components/mail/LargeFileRevokeDialog.vue create mode 100644 frontend/src/components/mail/MessageAttachments.large-files.spec.ts create mode 100644 frontend/src/components/mail/MessageLargeFileItem.vue create mode 100644 frontend/src/components/mail/MessageLargeFileLinkBar.vue create mode 100644 frontend/src/components/settings/LargeFileDeleteDialog.vue create mode 100644 frontend/src/components/settings/LargeFileLinkList.vue create mode 100644 frontend/src/components/settings/LargeFileUnsentList.vue create mode 100644 frontend/src/components/settings/LargeFileUsage.vue create mode 100644 frontend/src/components/share/ShareFileRow.vue create mode 100644 frontend/src/composables/useComposeLargeFiles.ts create mode 100644 frontend/src/composables/useLargeFileAlerts.ts create mode 100644 frontend/src/composables/useLargeFileScanRefresh.spec.ts create mode 100644 frontend/src/composables/useLargeFileScanRefresh.ts create mode 100644 frontend/src/composables/useLargeFileUploads.spec.ts create mode 100644 frontend/src/composables/useLargeFileUploads.ts create mode 100644 frontend/src/composables/useLargeFiles.ts create mode 100644 frontend/src/composables/usePublicShare.ts create mode 100644 frontend/src/pages/mail/ComposePage.large-files.spec.ts create mode 100644 frontend/src/pages/settings/MailLargeFilesPage.spec.ts create mode 100644 frontend/src/pages/settings/MailLargeFilesPage.vue create mode 100644 frontend/src/pages/share/SharePage.spec.ts create mode 100644 frontend/src/pages/share/SharePage.vue create mode 100644 frontend/src/router/index.spec.ts create mode 100644 frontend/src/shared/constants/large-file.ts create mode 100644 frontend/src/shared/types/large-file.ts create mode 100644 frontend/src/shared/utils/kst-date.spec.ts create mode 100644 frontend/src/shared/utils/kst-date.ts create mode 100644 frontend/src/shared/utils/large-file-status.spec.ts create mode 100644 frontend/src/shared/utils/large-file-status.ts create mode 100644 frontend/src/shared/utils/large-file.spec.ts create mode 100644 frontend/src/shared/utils/large-file.ts create mode 100644 frontend/src/shared/utils/share-links.spec.ts create mode 100644 frontend/src/shared/utils/share-links.ts create mode 100644 frontend/src/stores/large-files.store.ts diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 244a73b..2683bc5 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -6,6 +6,7 @@ import { RouterView } from 'vue-router' import SessionExpiredDialog from '@/components/feedback/SessionExpiredDialog.vue' import ToastHost from '@/components/feedback/ToastHost.vue' import { useClock } from '@/composables/useClock' +import { useLargeFileAlerts } from '@/composables/useLargeFileAlerts' import { useLogoutSync } from '@/composables/useLogoutBroadcast' import { useMailRealtime } from '@/composables/useMailRealtime' import { useNewMailNotifier } from '@/composables/useNewMailNotifier' @@ -15,9 +16,16 @@ useLogoutSync() // 실시간 새 메일 — 로그인한 동안 앱 전체에서 돈다(설정 등 다른 화면에서도 바탕화면 알림 · 연결 상태가 이어지게). // 메일 화면은 이 소식을 받아 목록 · 개수를 맞춘다 (계약 2절 "실시간 알림" — MailLayout 의 useMailStatePolling). -// 사용자가 웹메일 탭(어느 탭이든)으로 돌아오면 띄운 바탕화면 알림을 닫는다 +// 사용자가 웹메일 탭(어느 탭이든)으로 돌아오면 띄운 바탕화면 알림을 닫는다. +// 올린 대용량 파일의 위험 판정(11단계)도 이 연결로 온다 — 탭마다 알림 · 쓰기 화면의 그 파일을 "위험"으로 (useLargeFileAlerts) const newMailNotifier = useNewMailNotifier() -useMailRealtime({ clock: useClock(), onNewMail: newMailNotifier.notify, onUserReturn: newMailNotifier.dismiss }) +const largeFileAlerts = useLargeFileAlerts() +useMailRealtime({ + clock: useClock(), + onNewMail: newMailNotifier.notify, + onUserReturn: newMailNotifier.dismiss, + onLargeFile: largeFileAlerts.notify, +})