このコミットが含まれているのは:
2026-07-17 21:06:26 +09:00
コミット 3820d3d4d5
21個のファイルの変更949行の追加1270行の削除
+56 -36
ファイルの表示
@@ -6,11 +6,12 @@ import type { PostImportOrigin,
PostImportSkipReason,
StorageErrorHandler } from '@/lib/postImportTypes'
const SESSION_VERSION = 2
const SESSION_VERSION = 3
const SESSION_PREFIX = 'post-import-session:'
const CURRENT_SESSION_KEY = `${ SESSION_PREFIX }current`
const SOURCE_DRAFT_KEY = 'post-import-source-draft'
const SESSION_MAX_AGE_MS = 24 * 60 * 60 * 1000
const SESSION_ID_PATTERN =
/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
const ATTRIBUTE_KEYS = [
'title',
'thumbnailBase',
@@ -29,7 +30,21 @@ const isPlainObject = (value: unknown): value is Record<string, unknown> =>
typeof value === 'object' && value != null && !(Array.isArray (value))
const sessionKey = (): string => CURRENT_SESSION_KEY
export const validatePostImportSessionId = (value: unknown): string | null => {
if (typeof value !== 'string')
return null
if (value.length > 36 || !(SESSION_ID_PATTERN.test (value)))
return null
return value.toLowerCase ()
}
export const generatePostImportSessionId = (): string =>
crypto.randomUUID ()
const sessionKey = (sessionId: string): string => `${ SESSION_PREFIX }${ sessionId }`
const readStorage = (
@@ -283,9 +298,9 @@ const sanitiseRow = (value: unknown): PostImportRow | null => {
}
const isExpiredSession = (savedAt: string): boolean => {
const value = Date.parse (savedAt)
return Number.isNaN (value) || Date.now () - value > SESSION_MAX_AGE_MS
const isExpiredSession = (expiresAt: string): boolean => {
const value = Date.parse (expiresAt)
return Number.isNaN (value) || value <= Date.now ()
}
@@ -303,20 +318,25 @@ export const cleanupExpiredPostImportSessions = (
if (key == null || !(key.startsWith (SESSION_PREFIX)))
continue
const raw = sessionStorage.getItem (key)
if (raw == null)
continue
if (key !== CURRENT_SESSION_KEY)
const sessionId = validatePostImportSessionId (key.slice (SESSION_PREFIX.length))
if (sessionId == null)
{
sessionStorage.removeItem (key)
--i
continue
}
const raw = sessionStorage.getItem (key)
if (raw == null)
continue
try
{
const value = JSON.parse (raw) as { savedAt?: string }
if (typeof value.savedAt !== 'string' || isExpiredSession (value.savedAt))
const value = JSON.parse (raw) as { expiresAt?: string
rows?: unknown[] }
if (typeof value.expiresAt !== 'string'
|| isExpiredSession (value.expiresAt)
|| !(Array.isArray (value.rows)))
{
sessionStorage.removeItem (key)
--i
@@ -370,38 +390,33 @@ export const clearPostImportSourceDraft = (
export const savePostImportSession = (
sessionOrLegacyId: string | Omit<PostImportSession, 'version' | 'savedAt'>,
sessionOrOnError?: Omit<PostImportSession, 'version' | 'savedAt'> | StorageErrorHandler,
sessionId: string,
session: Omit<PostImportSession, 'version' | 'expiresAt'>,
onError?: StorageErrorHandler,
): boolean => {
const session =
typeof sessionOrLegacyId === 'string'
? sessionOrOnError as Omit<PostImportSession, 'version' | 'savedAt'>
: sessionOrLegacyId
const errorHandler =
typeof sessionOrLegacyId === 'string'
? onError
: sessionOrOnError as StorageErrorHandler | undefined
const validatedId = validatePostImportSessionId (sessionId)
if (validatedId == null)
return false
return writeStorage (
sessionKey (),
sessionKey (validatedId),
JSON.stringify ({
...session,
version: SESSION_VERSION,
savedAt: new Date ().toISOString () }),
errorHandler)
expiresAt: new Date (Date.now () + SESSION_MAX_AGE_MS).toISOString () }),
onError)
}
export const loadPostImportSession = (
legacyIdOrOnError?: string | StorageErrorHandler,
sessionId: string,
onError?: StorageErrorHandler,
): PostImportSession | null => {
const errorHandler =
typeof legacyIdOrOnError === 'string'
? onError
: legacyIdOrOnError
const raw = readStorage (sessionKey (), errorHandler)
const validatedId = validatePostImportSessionId (sessionId)
if (validatedId == null)
return null
const raw = readStorage (sessionKey (validatedId), onError)
if (raw == null)
return null
@@ -410,9 +425,9 @@ export const loadPostImportSession = (
const value = JSON.parse (raw) as Partial<PostImportSession>
if (value.version !== SESSION_VERSION || !(Array.isArray (value.rows)))
return null
if (typeof value.savedAt !== 'string' || isExpiredSession (value.savedAt))
if (typeof value.expiresAt !== 'string' || isExpiredSession (value.expiresAt))
{
removeStorage (sessionKey (), errorHandler)
removeStorage (sessionKey (validatedId), onError)
return null
}
@@ -422,7 +437,7 @@ export const loadPostImportSession = (
return {
version: SESSION_VERSION,
savedAt: value.savedAt,
expiresAt: value.expiresAt,
source: typeof value.source === 'string' ? value.source : '',
rows: rows as PostImportRow[],
repairMode: value.repairMode === 'failed' ? 'failed' : 'all' }
@@ -435,7 +450,12 @@ export const loadPostImportSession = (
export const clearPostImportSession = (
sessionId: string,
onError?: StorageErrorHandler,
) => {
removeStorage (sessionKey (), onError)
const validatedId = validatePostImportSessionId (sessionId)
if (validatedId == null)
return
removeStorage (sessionKey (validatedId), onError)
}