同步记录
This commit is contained in:
1 parent
818c980dec
commit
d647453085
10 files changed
+446
-72
No files matched your search
@@ -26,6 +26,7 @@ export function serializeBackupJob(job: BackupJob) {
|
||||
),
|
||||
error: job.error,
|
||||
createdAt: job.createdAt,
|
||||
updatedAt: job.updatedAt,
|
||||
finishedAt: job.finishedAt
|
||||
}
|
||||
}
|
||||
+32
-11
@@ -17,6 +17,7 @@ export interface BackupJob {
|
||||
outputPath: string | null
|
||||
error: string | null
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
finishedAt: string | null
|
||||
}
|
||||
|
||||
@@ -32,6 +33,7 @@ interface BackupJobRow {
|
||||
output_path: string | null
|
||||
error: string | null
|
||||
created_at: string
|
||||
updated_at: string
|
||||
finished_at: string | null
|
||||
}
|
||||
|
||||
@@ -55,6 +57,7 @@ function rowToJob(row: BackupJobRow): BackupJob {
|
||||
outputPath: row.output_path,
|
||||
error: row.error,
|
||||
createdAt: row.created_at,
|
||||
updatedAt: row.updated_at,
|
||||
finishedAt: row.finished_at
|
||||
}
|
||||
}
|
||||
@@ -69,7 +72,7 @@ export function getRunningBackupJob(userId?: number): BackupJob | null {
|
||||
ensureBackupSchema()
|
||||
const row = getDb().prepare(`
|
||||
SELECT id, user_id, type, status, progress, total, message, payload_json,
|
||||
output_path, error, created_at, finished_at
|
||||
output_path, error, created_at, updated_at, finished_at
|
||||
FROM backup_jobs
|
||||
WHERE status = 'running'${userId === undefined ? '' : ' AND user_id = ?'}
|
||||
LIMIT 1
|
||||
@@ -81,7 +84,7 @@ export function getActiveBackupJob(userId?: number): BackupJob | null {
|
||||
ensureBackupSchema()
|
||||
const row = getDb().prepare(`
|
||||
SELECT id, user_id, type, status, progress, total, message, payload_json,
|
||||
output_path, error, created_at, finished_at
|
||||
output_path, error, created_at, updated_at, finished_at
|
||||
FROM backup_jobs
|
||||
WHERE status IN ('pending', 'running')${userId === undefined ? '' : ' AND user_id = ?'}
|
||||
ORDER BY created_at DESC
|
||||
@@ -105,8 +108,8 @@ export function createBackupJob(input: {
|
||||
|
||||
getDb().prepare(`
|
||||
INSERT INTO backup_jobs
|
||||
(id, user_id, type, status, progress, total, message, payload_json, output_path, error, created_at, finished_at)
|
||||
VALUES (?, ?, ?, 'pending', 0, ?, ?, ?, NULL, NULL, ?, NULL)
|
||||
(id, user_id, type, status, progress, total, message, payload_json, output_path, error, created_at, updated_at, finished_at)
|
||||
VALUES (?, ?, ?, 'pending', 0, ?, ?, ?, NULL, NULL, ?, ?, NULL)
|
||||
`).run(
|
||||
id,
|
||||
userId,
|
||||
@@ -114,6 +117,7 @@ export function createBackupJob(input: {
|
||||
input.total ?? 0,
|
||||
input.message ?? '',
|
||||
payloadJson,
|
||||
now,
|
||||
now
|
||||
)
|
||||
|
||||
@@ -124,7 +128,7 @@ export function getBackupJob(id: string, userId?: number): BackupJob | null {
|
||||
ensureBackupSchema()
|
||||
const row = getDb().prepare(`
|
||||
SELECT id, user_id, type, status, progress, total, message, payload_json,
|
||||
output_path, error, created_at, finished_at
|
||||
output_path, error, created_at, updated_at, finished_at
|
||||
FROM backup_jobs
|
||||
WHERE id = ?${userId === undefined ? '' : ' AND user_id = ?'}
|
||||
`).get(...(userId === undefined ? [id] : [id, userId])) as BackupJobRow | undefined
|
||||
@@ -156,7 +160,7 @@ export function listBackupJobsPage(
|
||||
const total = countBackupJobs(userId)
|
||||
const rows = getDb().prepare(`
|
||||
SELECT id, user_id, type, status, progress, total, message, payload_json,
|
||||
output_path, error, created_at, finished_at
|
||||
output_path, error, created_at, updated_at, finished_at
|
||||
FROM backup_jobs
|
||||
${userId === undefined ? '' : 'WHERE user_id = ?'}
|
||||
ORDER BY created_at DESC
|
||||
@@ -169,7 +173,7 @@ export function listAllBackupJobsForUser(userId: number): BackupJob[] {
|
||||
ensureBackupSchema()
|
||||
const rows = getDb().prepare(`
|
||||
SELECT id, user_id, type, status, progress, total, message, payload_json,
|
||||
output_path, error, created_at, finished_at
|
||||
output_path, error, created_at, updated_at, finished_at
|
||||
FROM backup_jobs
|
||||
WHERE user_id = ?
|
||||
ORDER BY created_at DESC
|
||||
@@ -197,8 +201,8 @@ export function restoreBackupJobsAfterReplace(preserved: BackupJob[]): void {
|
||||
db.prepare('DELETE FROM backup_jobs').run()
|
||||
const insert = db.prepare(`
|
||||
INSERT INTO backup_jobs
|
||||
(id, user_id, type, status, progress, total, message, payload_json, output_path, error, created_at, finished_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
(id, user_id, type, status, progress, total, message, payload_json, output_path, error, created_at, updated_at, finished_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`)
|
||||
for (const job of preserved) {
|
||||
insert.run(
|
||||
@@ -213,6 +217,7 @@ export function restoreBackupJobsAfterReplace(preserved: BackupJob[]): void {
|
||||
job.outputPath,
|
||||
job.error,
|
||||
job.createdAt,
|
||||
job.updatedAt,
|
||||
job.finishedAt
|
||||
)
|
||||
}
|
||||
@@ -232,9 +237,10 @@ export function recoverInterruptedBackupJobs(): number {
|
||||
SET status = 'failed',
|
||||
error = '任务中断(服务重启或异常退出)',
|
||||
message = '任务已中断,请重试',
|
||||
updated_at = ?,
|
||||
finished_at = ?
|
||||
WHERE status = 'running'
|
||||
`).run(finishedAt)
|
||||
`).run(finishedAt, finishedAt)
|
||||
return Number(result.changes ?? 0)
|
||||
}
|
||||
|
||||
@@ -263,13 +269,14 @@ export function updateBackupJob(
|
||||
payload: patch.payload ?? current.payload,
|
||||
outputPath: patch.outputPath !== undefined ? patch.outputPath : current.outputPath,
|
||||
error: patch.error !== undefined ? patch.error : current.error,
|
||||
updatedAt: new Date().toISOString(),
|
||||
finishedAt: patch.finishedAt !== undefined ? patch.finishedAt : current.finishedAt
|
||||
}
|
||||
|
||||
getDb().prepare(`
|
||||
UPDATE backup_jobs
|
||||
SET status = ?, progress = ?, total = ?, message = ?, payload_json = ?,
|
||||
output_path = ?, error = ?, finished_at = ?
|
||||
output_path = ?, error = ?, updated_at = ?, finished_at = ?
|
||||
WHERE id = ?
|
||||
`).run(
|
||||
next.status,
|
||||
@@ -279,11 +286,25 @@ export function updateBackupJob(
|
||||
JSON.stringify(next.payload),
|
||||
next.outputPath,
|
||||
next.error,
|
||||
next.updatedAt,
|
||||
next.finishedAt,
|
||||
id
|
||||
)
|
||||
}
|
||||
|
||||
/** Update job data without changing its last activity timestamp. */
|
||||
export function updateBackupJobPayload(
|
||||
id: string,
|
||||
payload: Record<string, unknown>
|
||||
): void {
|
||||
ensureBackupSchema()
|
||||
getDb().prepare(`
|
||||
UPDATE backup_jobs
|
||||
SET payload_json = ?
|
||||
WHERE id = ?
|
||||
`).run(JSON.stringify(payload), id)
|
||||
}
|
||||
|
||||
export function markJobRunning(id: string, message?: string): void {
|
||||
updateBackupJob(id, {
|
||||
status: 'running',
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
markJobFailed,
|
||||
markJobRunning,
|
||||
recoverInterruptedBackupJobs,
|
||||
updateBackupJobPayload,
|
||||
type BackupJob
|
||||
} from './jobs'
|
||||
import {
|
||||
@@ -149,22 +150,41 @@ export function resumeSiteSyncJob(
|
||||
userId?: number | null
|
||||
): BackupJob {
|
||||
claimJobSlot()
|
||||
const job = getBackupJob(jobId, userId ?? undefined)
|
||||
const previousJob = getBackupJob(jobId, userId ?? undefined)
|
||||
if (
|
||||
!job
|
||||
|| job.type !== 'site_sync'
|
||||
|| job.status !== 'failed'
|
||||
|| job.payload.resumable !== true
|
||||
!previousJob
|
||||
|| previousJob.type !== 'site_sync'
|
||||
|| previousJob.status !== 'failed'
|
||||
|| previousJob.payload.resumable !== true
|
||||
) {
|
||||
throw new Error('SITE_SYNC_JOB_NOT_RESUMABLE')
|
||||
}
|
||||
const targetBaseUrl = typeof job.payload.targetBaseUrl === 'string'
|
||||
? job.payload.targetBaseUrl
|
||||
const targetBaseUrl = typeof previousJob.payload.targetBaseUrl === 'string'
|
||||
? previousJob.payload.targetBaseUrl
|
||||
: ''
|
||||
const conflictMode = job.payload.conflictMode === 'skip' ? 'skip' : 'overwrite'
|
||||
const direction = job.payload.direction === 'pull' ? 'pull' : 'push'
|
||||
const conflictMode = previousJob.payload.conflictMode === 'skip'
|
||||
? 'skip'
|
||||
: 'overwrite'
|
||||
const direction = previousJob.payload.direction === 'pull' ? 'pull' : 'push'
|
||||
if (!targetBaseUrl) throw new Error('SITE_SYNC_JOB_NOT_RESUMABLE')
|
||||
|
||||
const job = createBackupJob({
|
||||
type: 'site_sync',
|
||||
userId: previousJob.userId,
|
||||
total: previousJob.total,
|
||||
message: `继续任务 ${previousJob.id}…`,
|
||||
payload: {
|
||||
...previousJob.payload,
|
||||
resumedFromJobId: previousJob.id,
|
||||
resumedAt: new Date().toISOString(),
|
||||
cancelled: false
|
||||
}
|
||||
})
|
||||
updateBackupJobPayload(previousJob.id, {
|
||||
...previousJob.payload,
|
||||
resumable: false,
|
||||
supersededByJobId: job.id
|
||||
})
|
||||
registerSiteSyncToken(job.id, token)
|
||||
scheduleBackupJob(job, () => runSiteSyncJob(job.id, {
|
||||
targetBaseUrl,
|
||||
|
||||
@@ -24,11 +24,12 @@ function migrateBackupJobTypes(): void {
|
||||
output_path TEXT,
|
||||
error TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
finished_at TEXT
|
||||
);
|
||||
INSERT INTO backup_jobs_new
|
||||
(id, user_id, type, status, progress, total, message, payload_json, output_path, error, created_at, finished_at)
|
||||
SELECT id, user_id, type, status, progress, total, message, payload_json, output_path, error, created_at, finished_at
|
||||
(id, user_id, type, status, progress, total, message, payload_json, output_path, error, created_at, updated_at, finished_at)
|
||||
SELECT id, user_id, type, status, progress, total, message, payload_json, output_path, error, created_at, COALESCE(updated_at, created_at), finished_at
|
||||
FROM backup_jobs;
|
||||
DROP TABLE backup_jobs;
|
||||
ALTER TABLE backup_jobs_new RENAME TO backup_jobs;
|
||||
@@ -53,6 +54,7 @@ export function ensureBackupSchema(): void {
|
||||
output_path TEXT,
|
||||
error TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
finished_at TEXT
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_backup_jobs_created_at
|
||||
@@ -65,6 +67,9 @@ export function ensureBackupSchema(): void {
|
||||
if (!columns.some(column => column.name === 'user_id')) {
|
||||
getDb().exec('ALTER TABLE backup_jobs ADD COLUMN user_id INTEGER')
|
||||
}
|
||||
if (!columns.some(column => column.name === 'updated_at')) {
|
||||
getDb().exec('ALTER TABLE backup_jobs ADD COLUMN updated_at TEXT')
|
||||
}
|
||||
getDb().exec(`
|
||||
CREATE INDEX IF NOT EXISTS idx_backup_jobs_user_created
|
||||
ON backup_jobs(user_id, created_at DESC);
|
||||
@@ -74,6 +79,13 @@ export function ensureBackupSchema(): void {
|
||||
)
|
||||
WHERE user_id IS NULL
|
||||
AND EXISTS (SELECT 1 FROM users WHERE role = 'admin');
|
||||
UPDATE backup_jobs
|
||||
SET updated_at = COALESCE(finished_at, created_at)
|
||||
WHERE updated_at IS NULL;
|
||||
UPDATE backup_jobs
|
||||
SET updated_at = finished_at
|
||||
WHERE finished_at IS NOT NULL
|
||||
AND updated_at = created_at;
|
||||
`)
|
||||
migrateBackupJobTypes()
|
||||
}
|
||||
Reference in new issue
Block a user