fix(06.2): CR-04 WR-05 audit export functions use 401-refresh-retry and safe URL.revokeObjectURL
This commit is contained in:
+35
-19
@@ -395,7 +395,7 @@ export function adminListAuditLog({ start, end, user_handle, event_type, page =
|
|||||||
* the endpoint can authenticate the request (D-13, T-06.2-04-03).
|
* the endpoint can authenticate the request (D-13, T-06.2-04-03).
|
||||||
* Must NOT call res.json() — CSV is text/csv (Pitfall 5).
|
* Must NOT call res.json() — CSV is text/csv (Pitfall 5).
|
||||||
*/
|
*/
|
||||||
export async function adminExportAuditLogCsv(params = {}) {
|
export async function adminExportAuditLogCsv(params = {}, _retry = false) {
|
||||||
const { useAuthStore } = await import('../stores/auth.js')
|
const { useAuthStore } = await import('../stores/auth.js')
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
|
|
||||||
@@ -414,6 +414,18 @@ export async function adminExportAuditLogCsv(params = {}) {
|
|||||||
headers,
|
headers,
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (res.status === 401 && !_retry) {
|
||||||
|
try {
|
||||||
|
await authStore.refresh()
|
||||||
|
return adminExportAuditLogCsv(params, true)
|
||||||
|
} catch {
|
||||||
|
authStore.accessToken = null
|
||||||
|
authStore.user = null
|
||||||
|
throw new Error('Session expired')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!res.ok) throw new Error(`Export failed: ${res.status}`)
|
if (!res.ok) throw new Error(`Export failed: ${res.status}`)
|
||||||
|
|
||||||
const text = await res.text()
|
const text = await res.text()
|
||||||
@@ -422,8 +434,10 @@ export async function adminExportAuditLogCsv(params = {}) {
|
|||||||
const a = document.createElement('a')
|
const a = document.createElement('a')
|
||||||
a.href = url
|
a.href = url
|
||||||
a.download = 'audit-export.csv'
|
a.download = 'audit-export.csv'
|
||||||
|
document.body.appendChild(a)
|
||||||
a.click()
|
a.click()
|
||||||
URL.revokeObjectURL(url)
|
document.body.removeChild(a)
|
||||||
|
setTimeout(() => URL.revokeObjectURL(url), 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -431,22 +445,10 @@ export async function adminExportAuditLogCsv(params = {}) {
|
|||||||
*
|
*
|
||||||
* Returns: { items: [{ date: "YYYY-MM-DD", key: "audit-logs/YYYY-MM-DD.csv" }] }
|
* Returns: { items: [{ date: "YYYY-MM-DD", key: "audit-logs/YYYY-MM-DD.csv" }] }
|
||||||
* Items are sorted descending by date.
|
* Items are sorted descending by date.
|
||||||
|
* Routes through request() which has built-in 401-refresh-retry logic.
|
||||||
*/
|
*/
|
||||||
export async function adminListDailyExports() {
|
export function adminListDailyExports() {
|
||||||
const { useAuthStore } = await import('../stores/auth.js')
|
return request('/api/admin/audit-log/daily-exports')
|
||||||
const authStore = useAuthStore()
|
|
||||||
|
|
||||||
const headers = {}
|
|
||||||
if (authStore.accessToken) {
|
|
||||||
headers['Authorization'] = `Bearer ${authStore.accessToken}`
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await fetch('/api/admin/audit-log/daily-exports', {
|
|
||||||
headers,
|
|
||||||
credentials: 'include',
|
|
||||||
})
|
|
||||||
if (!res.ok) throw new Error(`Failed to list daily exports: ${res.status}`)
|
|
||||||
return res.json()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -457,7 +459,7 @@ export async function adminListDailyExports() {
|
|||||||
*
|
*
|
||||||
* @param {string} date — YYYY-MM-DD format date string
|
* @param {string} date — YYYY-MM-DD format date string
|
||||||
*/
|
*/
|
||||||
export async function adminDownloadDailyExport(date) {
|
export async function adminDownloadDailyExport(date, _retry = false) {
|
||||||
const { useAuthStore } = await import('../stores/auth.js')
|
const { useAuthStore } = await import('../stores/auth.js')
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
|
|
||||||
@@ -470,6 +472,18 @@ export async function adminDownloadDailyExport(date) {
|
|||||||
headers,
|
headers,
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (res.status === 401 && !_retry) {
|
||||||
|
try {
|
||||||
|
await authStore.refresh()
|
||||||
|
return adminDownloadDailyExport(date, true)
|
||||||
|
} catch {
|
||||||
|
authStore.accessToken = null
|
||||||
|
authStore.user = null
|
||||||
|
throw new Error('Session expired')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!res.ok) throw new Error(`Download failed: ${res.status}`)
|
if (!res.ok) throw new Error(`Download failed: ${res.status}`)
|
||||||
|
|
||||||
const text = await res.text()
|
const text = await res.text()
|
||||||
@@ -478,8 +492,10 @@ export async function adminDownloadDailyExport(date) {
|
|||||||
const a = document.createElement('a')
|
const a = document.createElement('a')
|
||||||
a.href = url
|
a.href = url
|
||||||
a.download = `audit-${date}.csv`
|
a.download = `audit-${date}.csv`
|
||||||
|
document.body.appendChild(a)
|
||||||
a.click()
|
a.click()
|
||||||
URL.revokeObjectURL(url)
|
document.body.removeChild(a)
|
||||||
|
setTimeout(() => URL.revokeObjectURL(url), 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Document content proxy URL ────────────────────────────────────────────────
|
// ── Document content proxy URL ────────────────────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user