feat: Google Drive restore endpoints (Faz 3)

GET /api/google-drive/backup-files: kullanıcının senkronize
edilmiş dosyalarının listesi (PRD §28 "N dosya bulundu"). GET
/download: bir dosyayı Drive'dan proxy'leyip ham baytları döner —
istenen drive_file_id'nin gerçekten bu kullanıcıya ait olduğu
önce doğrulanıyor (PRD §39, başka kullanıcının backup'ı sızmasın).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
mstfyldz
2026-08-10 04:24:56 +03:00
co-authored by Claude Sonnet 5
parent 313d5b72ef
commit ce6fa9deae
3 changed files with 77 additions and 1 deletions
+59
View File
@@ -322,3 +322,62 @@ export const getBackupStatus = async (req: AuthenticatedRequest, res: Response)
res.status(500).json({ error: 'Yedekleme durumu alınamadı.' });
}
};
// Faz 3 — Restore. PRD §28: "Google Drive Backup — N dosya bulundu [Geri Yükle]".
// Electron'un restoreQueue.js'i bu listeyi çekip yerelde hangi dosyaların eksik
// olduğuna kendisi karar veriyor (backend local diski bilmiyor).
export const listBackupFiles = async (req: AuthenticatedRequest, res: Response) => {
try {
const userId = req.user?.id;
const { data, error } = await supabase
.from('google_drive_files')
.select('local_path, file_name, drive_file_id, file_size, drive_modified_at')
.eq('user_id', userId)
.eq('status', 'synced')
.order('local_path', { ascending: true });
if (error) throw error;
res.json({ files: data || [] });
} catch (error: any) {
console.error('[DRIVE] List Backup Files Error:', error);
res.status(500).json({ error: 'Yedeklenen dosya listesi alınamadı.' });
}
};
// Tek bir dosyayı Drive'dan indirip ham baytları döner. PRD §39: kullanıcı
// başka bir kullanıcının backup'ını indiremesin — bu yüzden istenen
// drive_file_id'nin gerçekten bu kullanıcının google_drive_files kaydında
// olduğu ÖNCE doğrulanıyor (aksi halde herhangi bir Drive dosya ID'si verilip
// backend'in geçerli token'ı üzerinden başkasının verisi sızdırılabilirdi).
export const downloadFile = async (req: AuthenticatedRequest, res: Response) => {
try {
const userId = req.user?.id as string;
const driveFileId = String(req.query.drive_file_id || '');
if (!driveFileId) {
return res.status(400).json({ error: 'drive_file_id zorunludur.' });
}
const { data: fileRow } = await supabase
.from('google_drive_files')
.select('id, file_name')
.eq('user_id', userId)
.eq('drive_file_id', driveFileId)
.maybeSingle();
if (!fileRow) {
return res.status(403).json({ error: 'Bu dosyaya erişim yetkiniz yok.' });
}
let accessToken: string;
try {
accessToken = await getValidAccessToken(userId);
} catch (err: any) {
return res.status(409).json({ error: err.message || 'Google Drive bağlantınızın yenilenmesi gerekiyor.' });
}
const buffer = await googleDrive.downloadFile(accessToken, driveFileId);
res.setHeader('Content-Type', 'application/octet-stream');
res.send(buffer);
} catch (error: any) {
console.error('[DRIVE] Download Error:', error);
res.status(500).json({ error: 'Dosya indirilemedi.' });
}
};